From 8e180983b3d46ebcf458df35492a0c1b165f9354 Mon Sep 17 00:00:00 2001 From: Chris Gross Date: Tue, 26 Nov 2013 15:01:52 -0500 Subject: [PATCH 001/161] Updated grunt build to provide a less.js/less.min.js w/o version number. Also added automatic updating of ver from package.json to bower.json. --- Gruntfile.js | 23 ++++++++++++++++++++++- bower.json | 6 +++--- package.json | 9 +++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0a2fa4262..1ef384827 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,4 +1,5 @@ 'use strict'; +var fs = require('fs'); module.exports = function(grunt) { @@ -88,6 +89,17 @@ module.exports = function(grunt) { } }, + copy: { + stable: { + src: 'dist/less-<%= pkg.version %>.js', + dest: 'dist/less.js' + }, + stable_min: { + src: 'dist/less-<%= pkg.version %>.min.js', + dest: 'dist/less.min.js' + } + }, + jshint: { options: {jshintrc: '.jshintrc'}, files: { @@ -227,7 +239,10 @@ module.exports = function(grunt) { // Release grunt.registerTask('stable', [ 'concat:stable', - 'uglify:stable' + 'uglify:stable', + 'copy:stable', + 'copy:stable_min', + 'updateBowerJson' ]); // Run all browser tests @@ -270,4 +285,10 @@ module.exports = function(grunt) { grunt.registerTask('readme', [ 'concat:readme' ]); + + grunt.registerTask('updateBowerJson', function() { + var bowerJson = require('./bower.json'); + bowerJson.version = grunt.config('pkg.version'); + fs.writeFileSync('./bower.json', JSON.stringify(bowerJson,null,2)); + }); }; diff --git a/bower.json b/bower.json index 482862ddb..88ab85e7d 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "less", - "version": "1.5.0", - "main": "./dist/less-1.5.0.js", + "version": "1.5.1", + "main": "./dist/less.js", "ignore": [ "**/.*", "benchmark", @@ -15,4 +15,4 @@ "package.json", "bower.json" ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index 16d2bb72f..a75291f78 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "diff": "~1.0", - "grunt": "~0.4.1", + "grunt": "~0.4.2", "grunt-contrib-clean": "~0.5.0", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-connect": "~0.3.0", @@ -58,8 +58,9 @@ "grunt-shell": "~0.3.1", "http-server": "~0.5.5", "matchdep": "~0.1.2", - "time-grunt": "~0.1.1" - }, + "time-grunt": "~0.1.1", + "grunt-contrib-copy": "~0.4.1" + }, "keywords": [ "compile less", "css nesting", @@ -85,4 +86,4 @@ "variables in css", "css less" ] -} \ No newline at end of file +} From 335955fc3638b4afe93352dbff2c2501cc92809e Mon Sep 17 00:00:00 2001 From: Chris Gross Date: Tue, 26 Nov 2013 17:02:29 -0500 Subject: [PATCH 002/161] removed all version numbered files from dist --- Gruntfile.js | 17 +- dist/less-1.1.0.js | 2695 -------------- dist/less-1.1.0.min.js | 16 - dist/less-1.1.1.js | 2710 -------------- dist/less-1.1.1.min.js | 16 - dist/less-1.1.2.js | 2712 -------------- dist/less-1.1.2.min.js | 16 - dist/less-1.1.3.js | 2721 -------------- dist/less-1.1.3.min.js | 16 - dist/less-1.1.4.js | 2769 -------------- dist/less-1.1.4.min.js | 16 - dist/less-1.1.5.js | 2805 -------------- dist/less-1.1.5.min.js | 9 - dist/less-1.1.6.js | 3004 --------------- dist/less-1.1.6.min.js | 9 - dist/less-1.2.0.js | 3293 ----------------- dist/less-1.2.0.min.js | 9 - dist/less-1.2.1.js | 3318 ----------------- dist/less-1.2.1.min.js | 9 - dist/less-1.2.2.js | 3337 ----------------- dist/less-1.2.2.min.js | 9 - dist/less-1.3.0.js | 3478 ------------------ dist/less-1.3.0.min.js | 9 - dist/less-1.3.1.js | 4011 -------------------- dist/less-1.3.1.min.js | 9 - dist/less-1.3.2.js | 4401 ---------------------- dist/less-1.3.2.min.js | 9 - dist/less-1.3.3.js | 4413 ---------------------- dist/less-1.3.3.min.js | 9 - dist/less-1.4.0-beta.js | 5830 ----------------------------- dist/less-1.4.0-beta.min.js | 11 - dist/less-1.4.0.js | 5830 ----------------------------- dist/less-1.4.0.min.js | 11 - dist/less-1.4.1.js | 5837 ----------------------------- dist/less-1.4.1.min.js | 11 - dist/less-1.4.2.js | 5837 ----------------------------- dist/less-1.4.2.min.js | 11 - dist/less-1.5.0.js | 6914 ---------------------------------- dist/less-1.5.0.min.js | 13 - dist/less-1.5.1.js | 6941 ----------------------------------- dist/less-1.5.1.min.js | 13 - dist/less-rhino-1.1.3.js | 2460 ------------- dist/less-rhino-1.1.5.js | 2481 ------------- dist/less-rhino-1.3.1.js | 3725 ------------------- dist/less-rhino-1.3.2.js | 3990 -------------------- dist/less-rhino-1.3.3.js | 4002 -------------------- dist/less-rhino-1.4.0.js | 4273 --------------------- 47 files changed, 2 insertions(+), 104033 deletions(-) delete mode 100644 dist/less-1.1.0.js delete mode 100644 dist/less-1.1.0.min.js delete mode 100644 dist/less-1.1.1.js delete mode 100644 dist/less-1.1.1.min.js delete mode 100644 dist/less-1.1.2.js delete mode 100644 dist/less-1.1.2.min.js delete mode 100644 dist/less-1.1.3.js delete mode 100644 dist/less-1.1.3.min.js delete mode 100644 dist/less-1.1.4.js delete mode 100644 dist/less-1.1.4.min.js delete mode 100644 dist/less-1.1.5.js delete mode 100644 dist/less-1.1.5.min.js delete mode 100644 dist/less-1.1.6.js delete mode 100644 dist/less-1.1.6.min.js delete mode 100644 dist/less-1.2.0.js delete mode 100644 dist/less-1.2.0.min.js delete mode 100644 dist/less-1.2.1.js delete mode 100644 dist/less-1.2.1.min.js delete mode 100644 dist/less-1.2.2.js delete mode 100644 dist/less-1.2.2.min.js delete mode 100644 dist/less-1.3.0.js delete mode 100644 dist/less-1.3.0.min.js delete mode 100644 dist/less-1.3.1.js delete mode 100644 dist/less-1.3.1.min.js delete mode 100644 dist/less-1.3.2.js delete mode 100644 dist/less-1.3.2.min.js delete mode 100644 dist/less-1.3.3.js delete mode 100644 dist/less-1.3.3.min.js delete mode 100644 dist/less-1.4.0-beta.js delete mode 100644 dist/less-1.4.0-beta.min.js delete mode 100644 dist/less-1.4.0.js delete mode 100644 dist/less-1.4.0.min.js delete mode 100644 dist/less-1.4.1.js delete mode 100644 dist/less-1.4.1.min.js delete mode 100644 dist/less-1.4.2.js delete mode 100644 dist/less-1.4.2.min.js delete mode 100644 dist/less-1.5.0.js delete mode 100644 dist/less-1.5.0.min.js delete mode 100644 dist/less-1.5.1.js delete mode 100644 dist/less-1.5.1.min.js delete mode 100644 dist/less-rhino-1.1.3.js delete mode 100644 dist/less-rhino-1.1.5.js delete mode 100644 dist/less-rhino-1.3.1.js delete mode 100644 dist/less-rhino-1.3.2.js delete mode 100644 dist/less-rhino-1.3.3.js delete mode 100644 dist/less-rhino-1.4.0.js diff --git a/Gruntfile.js b/Gruntfile.js index 1ef384827..a27a842ba 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -58,7 +58,7 @@ module.exports = function(grunt) { }, stable: { src: ['<%= build.browser %>'], - dest: 'dist/less-<%= pkg.version %>.js' + dest: 'dist/less.js' }, // Rhino rhino: { @@ -67,7 +67,7 @@ module.exports = function(grunt) { footer: '' // override task-level footer }, src: ['<%= build.rhino %>'], - dest: 'dist/less-rhino-<%= pkg.version %>.js' + dest: 'dist/less-rhino.js' }, // Generate readme readme: { @@ -85,17 +85,6 @@ module.exports = function(grunt) { }, stable: { src: ['<%= concat.stable.dest %>'], - dest: 'dist/less-<%= pkg.version %>.min.js' - } - }, - - copy: { - stable: { - src: 'dist/less-<%= pkg.version %>.js', - dest: 'dist/less.js' - }, - stable_min: { - src: 'dist/less-<%= pkg.version %>.min.js', dest: 'dist/less.min.js' } }, @@ -240,8 +229,6 @@ module.exports = function(grunt) { grunt.registerTask('stable', [ 'concat:stable', 'uglify:stable', - 'copy:stable', - 'copy:stable_min', 'updateBowerJson' ]); diff --git a/dist/less-1.1.0.js b/dist/less-1.1.0.js deleted file mode 100644 index 487c06acd..000000000 --- a/dist/less-1.1.0.js +++ /dev/null @@ -1,2695 +0,0 @@ -// -// LESS - Leaner CSS v1.1.0 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function (window, undefined) { -// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -// ecma-5.js -// -// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -// -- tlrobinson Tom Robinson -// dantman Daniel Friesen - -// -// Array -// -if (!Array.isArray) { - Array.isArray = function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]" || - (obj instanceof Array); - }; -} -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(block, thisObject) { - var len = this.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof(window) === 'undefined') { - less = exports, - tree = require('less/tree'); -} else { - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '&' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) { - i += match[0].length - 1; - selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])]; - } else { - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page|@-[-a-z]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (typeof(window) !== 'undefined') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('less/tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args) { - this.name = name; - this.args = args; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - return tree.functions[this.name].apply(tree.functions, args); - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else if (rgb.length == 8) { - this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0; - this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('less/tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Element = function (combinator, value) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value.trim(); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('less/tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else { - return this.value[0].eval(env); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - toCSS: function () { - if (this.escaped) { - return this.evaluated; - } else { - return JSON.stringify(this.evaluated); - } - }, - eval: function (env) { - var result, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return new(tree.Variable)('@' + name).eval(env).value; - }); - - expression = new(Function)('return (' + expression + ')'); - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - this.evaluated = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - return this; - } -}; - -})(require('less/tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('less/tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(this.arguments, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - this.value = this.value.replace(/@\{([\w-]+)\}/g, function (_, name) { - return new(tree.Variable)('@' + name).eval(env).value; - }).replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, this.index, true).eval(env).toCSS(); - }); - return this; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - for (var s = 0; s < this.selectors.length; s++) { - for (var c = 0; c < context.length; c++) { - paths.push(context[c].concat([this.selectors[s]])); - } - } - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - } -}; -})(require('less/tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - if (this.elements[0].value === other.elements[0].value) { - return true; - } else { - return false; - } -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('less/tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('less/tree')); -require('less/tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - styles[i].type = 'text/css'; - styles[i].innerHTML = tree.toCSS(); - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = [''].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.0.min.js b/dist/less-1.1.0.min.js deleted file mode 100644 index ede454e10..000000000 --- a/dist/less-1.1.0.min.js +++ /dev/null @@ -1,16 +0,0 @@ -// -// LESS - Leaner CSS v1.1.0 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -// -// LESS - Leaner CSS v1.1.0 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function v(a,b){var c="less-error-message:"+p(b),e=[""].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,q([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}function u(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function t(a){return a&&a.parentNode.removeChild(a)}function s(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){u("browser doesn't support AJAX.");return null}}function r(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=s(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function q(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||p(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(u("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function p(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i),r(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())q(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type})).parse(a,function(a,d){if(a)return v(a,i);try{c(d,b,{local:!1,lastModified:g,remaining:f}),t(document.getElementById("less-error-message:"+p(i)))}catch(a){v(a,i)}})}catch(h){v(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function n(a,b){for(var c=0;c>>0;for(var d=0;d>>0,c=Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else for(;;){if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}for(;c=b)return-1;c<0&&(c+=b);for(;ck&&(j[f]=j[f].slice(c-k),k=c)}function q(){j[f]=g,c=h,k=c}function p(){g=j[f],h=c,k=c}var b,c,f,g,h,i,j,k,l,m=this,n=function(){},o=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=a,c(a),e.queue.length===0&&n()},a)}};this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null;return l={imports:o,parse:function(d,g){var h,l,m,o,p,q,r=[],t,u=null;c=f=k=i=0,j=[],b=d.replace(/\r\n/g,"\n"),j=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b]));return new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)==="/"){if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)}},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==='"'||b.charAt(d)==="'"){f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)}},keyword:function(){var a;if(a=s(/^[A-Za-z-]+/))return new e.Keyword(a)},call:function(){var a,b;if(!!(a=/^([\w-]+|%)\(/.exec(j[f]))){a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b)}},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)==="u"&&!!s(/^url\(/)){a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)}},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(!(d>57||d<45||d===47))if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==="`"){f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!!t(/^[@\w.%-]+\/[@\w.-]+/)&&(a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i==="."||i==="#"){while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)}},definition:function(){var a,d=[],f,g,h,i;if(!(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/)))if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!!s(/^opacity=/i))if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=s(this.combinator),a=s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/);if(a)return new e.Element(c,a)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="&"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!!s("[")){if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"}},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,g;p();if(g=/^([.#: \w-]+)[\s\n]*\{/.exec(j[f]))c+=g[0].length-1,a=[new e.Selector([new e.Element(null,g[1])])];else while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g!=="."&&g!=="#"&&g!=="&")if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)==="@"){if(d=s(this["import"]))return d;if(a=s(/^@media|@page|@-[-a-z]+/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)}},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}},typeof a!="undefined"&&(d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)}),function(a){function d(a){return Math.min(1,Math.max(0,a))}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){a=a<0?a+1:a>1?a-1:a;return a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();e.s+=c.value/100,e.s=d(e.s);return b(e)},desaturate:function(a,c){var e=a.toHSL();e.s-=c.value/100,e.s=d(e.s);return b(e)},lighten:function(a,c){var e=a.toHSL();e.l+=c.value/100,e.l=d(e.l);return b(e)},darken:function(a,c){var e=a.toHSL();e.l-=c.value/100,e.l=d(e.l);return b(e)},fadein:function(a,c){var e=a.toHSL();e.a+=c.value/100,e.a=d(e.a);return b(e)},fadeout:function(a,c){var e=a.toHSL();e.a-=c.value/100,e.a=d(e.a);return b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;d.h=e<0?360+e:e;return b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16);return a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b":a.compress?">":" > "}[this.value]}}(c("less/tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value[0].eval(b)},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("less/tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){for(var f=0;f0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;e1?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}});return this._lookups[g]=d},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;if(!this.root)if(b.length===0)g=this.selectors.map(function(a){return[a]});else for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f);return d.join("")+(c.compress?"\n":"")}}}(c("less/tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){return this.elements[0].value===a.elements[0].value?!0:!1},a.Selector.prototype.toCSS=function(a){if(this._css)return this._css;return this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("less/tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(!/^(?:https?:\/|file:\/|data:\/)?\//.test(b.value)&&c.length>0&&typeof a!="undefined"&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("less/tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("less/tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("less/tree")),c("less/tree").find=function(a,b){for(var c=0,d;c0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c){a&&q(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof(window) === 'undefined') { - less = exports, - tree = require('less/tree'); -} else { - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '&' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) { - i += match[0].length - 1; - selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])]; - } else { - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page|@-[-a-z]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (typeof(window) !== 'undefined') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('less/tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args) { - this.name = name; - this.args = args; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - return tree.functions[this.name].apply(tree.functions, args); - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else if (rgb.length == 8) { - this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0; - this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('less/tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Element = function (combinator, value) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value.trim(); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('less/tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('less/tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('less/tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(this.arguments, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - this.value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - return new(tree.Variable)('@' + name, that.index).eval(env).value; - }); - return this; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - for (var s = 0; s < this.selectors.length; s++) { - for (var c = 0; c < context.length; c++) { - paths.push(context[c].concat([this.selectors[s]])); - } - } - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - } -}; -})(require('less/tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - if (this.elements[0].value === other.elements[0].value) { - return true; - } else { - return false; - } -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('less/tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('less/tree')); -require('less/tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('less/tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - styles[i].type = 'text/css'; - styles[i].innerHTML = tree.toCSS(); - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.1.min.js b/dist/less-1.1.1.min.js deleted file mode 100644 index c204123ec..000000000 --- a/dist/less-1.1.1.min.js +++ /dev/null @@ -1,16 +0,0 @@ -// -// LESS - Leaner CSS v1.1.1 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -// -// LESS - Leaner CSS v1.1.1 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function v(a,b){var c="less-error-message:"+p(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,q([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}function u(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function t(a){return a&&a.parentNode.removeChild(a)}function s(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){u("browser doesn't support AJAX.");return null}}function r(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=s(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function q(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||p(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(u("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function p(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i),r(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())q(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type})).parse(a,function(a,d){if(a)return v(a,i);try{c(d,b,{local:!1,lastModified:g,remaining:f}),t(document.getElementById("less-error-message:"+p(i)))}catch(a){v(a,i)}})}catch(h){v(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function n(a,b){for(var c=0;c>>0;for(var d=0;d>>0,c=Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else for(;;){if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}for(;c=b)return-1;c<0&&(c+=b);for(;ck&&(j[f]=j[f].slice(c-k),k=c)}function q(){j[f]=g,c=h,k=c}function p(){g=j[f],h=c,k=c}var b,c,f,g,h,i,j,k,l,m=this,n=function(){},o=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=a,c(a),e.queue.length===0&&n()},a)}};this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null;return l={imports:o,parse:function(d,g){var h,l,m,o,p,q,r=[],t,u=null;c=f=k=i=0,j=[],b=d.replace(/\r\n/g,"\n"),j=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b]));return new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)==="/"){if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)}},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==='"'||b.charAt(d)==="'"){f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)}},keyword:function(){var a;if(a=s(/^[A-Za-z-]+/))return new e.Keyword(a)},call:function(){var a,b;if(!!(a=/^([\w-]+|%)\(/.exec(j[f]))){a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b)}},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)==="u"&&!!s(/^url\(/)){a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)}},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(!(d>57||d<45||d===47))if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==="`"){f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!!t(/^[@\w.%-]+\/[@\w.-]+/)&&(a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i==="."||i==="#"){while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)}},definition:function(){var a,d=[],f,g,h,i;if(!(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/)))if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!!s(/^opacity=/i))if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=s(this.combinator),a=s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/);if(a)return new e.Element(c,a)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="&"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!!s("[")){if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"}},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,g;p();if(g=/^([.#: \w-]+)[\s\n]*\{/.exec(j[f]))c+=g[0].length-1,a=[new e.Selector([new e.Element(null,g[1])])];else while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g!=="."&&g!=="#"&&g!=="&")if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)==="@"){if(d=s(this["import"]))return d;if(a=s(/^@media|@page|@-[-a-z]+/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)}},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}},typeof a!="undefined"&&(d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)}),function(a){function d(a){return Math.min(1,Math.max(0,a))}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){a=a<0?a+1:a>1?a-1:a;return a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();e.s+=c.value/100,e.s=d(e.s);return b(e)},desaturate:function(a,c){var e=a.toHSL();e.s-=c.value/100,e.s=d(e.s);return b(e)},lighten:function(a,c){var e=a.toHSL();e.l+=c.value/100,e.l=d(e.l);return b(e)},darken:function(a,c){var e=a.toHSL();e.l-=c.value/100,e.l=d(e.l);return b(e)},fadein:function(a,c){var e=a.toHSL();e.a+=c.value/100,e.a=d(e.a);return b(e)},fadeout:function(a,c){var e=a.toHSL();e.a-=c.value/100,e.a=d(e.a);return b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;d.h=e<0?360+e:e;return b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16);return a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b":a.compress?">":" > "}[this.value]}}(c("less/tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("less/tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){for(var f=0;f0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;e1?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}});return this._lookups[g]=d},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;if(!this.root)if(b.length===0)g=this.selectors.map(function(a){return[a]});else for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f);return d.join("")+(c.compress?"\n":"")}}}(c("less/tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){return this.elements[0].value===a.elements[0].value?!0:!1},a.Selector.prototype.toCSS=function(a){if(this._css)return this._css;return this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("less/tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(!/^(?:https?:\/|file:\/|data:\/)?\//.test(b.value)&&c.length>0&&typeof a!="undefined"&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("less/tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("less/tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("less/tree" -)),c("less/tree").find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c){a&&q(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof(window) === 'undefined') { - less = exports, - tree = require('less/tree'); -} else { - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '&' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) { - i += match[0].length - 1; - selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])]; - } else { - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page|@-[-a-z]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (typeof(window) !== 'undefined') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('less/tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args) { - this.name = name; - this.args = args; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - return tree.functions[this.name].apply(tree.functions, args); - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else if (rgb.length == 8) { - this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0; - this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('less/tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Element = function (combinator, value) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value.trim(); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('less/tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('less/tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('less/tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - for (var s = 0; s < this.selectors.length; s++) { - for (var c = 0; c < context.length; c++) { - paths.push(context[c].concat([this.selectors[s]])); - } - } - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - } -}; -})(require('less/tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - if (this.elements[0].value === other.elements[0].value) { - return true; - } else { - return false; - } -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('less/tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('less/tree')); -require('less/tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('less/tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - styles[i].type = 'text/css'; - styles[i].innerHTML = tree.toCSS(); - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.2.min.js b/dist/less-1.1.2.min.js deleted file mode 100644 index 9b2fc8a43..000000000 --- a/dist/less-1.1.2.min.js +++ /dev/null @@ -1,16 +0,0 @@ -// -// LESS - Leaner CSS v1.1.2 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -// -// LESS - Leaner CSS v1.1.2 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function v(a,b){var c="less-error-message:"+p(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,q([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}function u(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function t(a){return a&&a.parentNode.removeChild(a)}function s(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){u("browser doesn't support AJAX.");return null}}function r(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=s(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function q(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||p(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(u("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function p(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i),r(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())q(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type})).parse(a,function(a,d){if(a)return v(a,i);try{c(d,b,{local:!1,lastModified:g,remaining:f}),t(document.getElementById("less-error-message:"+p(i)))}catch(a){v(a,i)}})}catch(h){v(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function n(a,b){for(var c=0;c>>0;for(var d=0;d>>0,c=Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else for(;;){if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}for(;c=b)return-1;c<0&&(c+=b);for(;ck&&(j[f]=j[f].slice(c-k),k=c)}function q(){j[f]=g,c=h,k=c}function p(){g=j[f],h=c,k=c}var b,c,f,g,h,i,j,k,l,m=this,n=function(){},o=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=a,c(a),e.queue.length===0&&n()},a)}};this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null;return l={imports:o,parse:function(d,g){var h,l,m,o,p,q,r=[],t,u=null;c=f=k=i=0,j=[],b=d.replace(/\r\n/g,"\n"),j=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b]));return new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)==="/"){if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)}},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==='"'||b.charAt(d)==="'"){f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)}},keyword:function(){var a;if(a=s(/^[A-Za-z-]+/))return new e.Keyword(a)},call:function(){var a,b;if(!!(a=/^([\w-]+|%)\(/.exec(j[f]))){a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b)}},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)==="u"&&!!s(/^url\(/)){a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)}},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(!(d>57||d<45||d===47))if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==="`"){f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!!t(/^[@\w.%-]+\/[@\w.-]+/)&&(a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i==="."||i==="#"){while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)}},definition:function(){var a,d=[],f,g,h,i;if(!(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/)))if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!!s(/^opacity=/i))if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=s(this.combinator),a=s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/);if(a)return new e.Element(c,a)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="&"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!!s("[")){if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"}},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,g;p();if(g=/^([.#: \w-]+)[\s\n]*\{/.exec(j[f]))c+=g[0].length-1,a=[new e.Selector([new e.Element(null,g[1])])];else while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g!=="."&&g!=="#"&&g!=="&")if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)==="@"){if(d=s(this["import"]))return d;if(a=s(/^@media|@page|@-[-a-z]+/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)}},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}},typeof a!="undefined"&&(d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)}),function(a){function d(a){return Math.min(1,Math.max(0,a))}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){a=a<0?a+1:a>1?a-1:a;return a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();e.s+=c.value/100,e.s=d(e.s);return b(e)},desaturate:function(a,c){var e=a.toHSL();e.s-=c.value/100,e.s=d(e.s);return b(e)},lighten:function(a,c){var e=a.toHSL();e.l+=c.value/100,e.l=d(e.l);return b(e)},darken:function(a,c){var e=a.toHSL();e.l-=c.value/100,e.l=d(e.l);return b(e)},fadein:function(a,c){var e=a.toHSL();e.a+=c.value/100,e.a=d(e.a);return b(e)},fadeout:function(a,c){var e=a.toHSL();e.a-=c.value/100,e.a=d(e.a);return b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;d.h=e<0?360+e:e;return b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16);return a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b":a.compress?">":" > "}[this.value]}}(c("less/tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("less/tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;e1?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}});return this._lookups[g]=d},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;if(!this.root)if(b.length===0)g=this.selectors.map(function(a){return[a]});else for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f);return d.join("")+(c.compress?"\n":"")}}}(c("less/tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){return this.elements[0].value===a.elements[0].value?!0:!1},a.Selector.prototype.toCSS=function(a){if(this._css)return this._css;return this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("less/tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(!/^(?:https?:\/|file:\/|data:\/)?\//.test(b.value)&&c.length>0&&typeof a!="undefined"&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("less/tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("less/tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){ -if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("less/tree")),c("less/tree").find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c){a&&q(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof(window) === 'undefined') { - less = exports, - tree = require('less/tree'); -} else { - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '&' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) { - i += match[0].length - 1; - selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])]; - } else { - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page|@-[-a-z]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (typeof(window) !== 'undefined') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('less/tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { message: "error evaluating function `" + this.name + "`", - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else if (rgb.length == 8) { - this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0; - this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('less/tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Element = function (combinator, value) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value.trim(); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('less/tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('less/tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('less/tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - for (var s = 0; s < this.selectors.length; s++) { - for (var c = 0; c < context.length; c++) { - paths.push(context[c].concat([this.selectors[s]])); - } - } - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - } -}; -})(require('less/tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - if (this.elements[0].value === other.elements[0].value) { - return true; - } else { - return false; - } -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('less/tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('less/tree')); -require('less/tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('less/tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - styles[i].type = 'text/css'; - styles[i].innerHTML = tree.toCSS(); - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.3.min.js b/dist/less-1.1.3.min.js deleted file mode 100644 index 6e4d5cff5..000000000 --- a/dist/less-1.1.3.min.js +++ /dev/null @@ -1,16 +0,0 @@ -// -// LESS - Leaner CSS v1.1.3 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -// -// LESS - Leaner CSS v1.1.3 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function v(a,b){var c="less-error-message:"+p(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,q([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}function u(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function t(a){return a&&a.parentNode.removeChild(a)}function s(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){u("browser doesn't support AJAX.");return null}}function r(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=s(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function q(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||p(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(u("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function p(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i),r(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())q(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type})).parse(a,function(a,d){if(a)return v(a,i);try{c(d,b,{local:!1,lastModified:g,remaining:f}),t(document.getElementById("less-error-message:"+p(i)))}catch(a){v(a,i)}})}catch(h){v(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function n(a,b){for(var c=0;c>>0;for(var d=0;d>>0,c=Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else for(;;){if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}for(;c=b)return-1;c<0&&(c+=b);for(;ck&&(j[f]=j[f].slice(c-k),k=c)}function q(){j[f]=g,c=h,k=c}function p(){g=j[f],h=c,k=c}var b,c,f,g,h,i,j,k,l,m=this,n=function(){},o=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=a,c(a),e.queue.length===0&&n()},a)}};this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null;return l={imports:o,parse:function(d,g){var h,l,m,o,p,q,r=[],t,u=null;c=f=k=i=0,j=[],b=d.replace(/\r\n/g,"\n"),j=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b]));return new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)==="/"){if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)}},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==='"'||b.charAt(d)==="'"){f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)}},keyword:function(){var a;if(a=s(/^[A-Za-z-]+/))return new e.Keyword(a)},call:function(){var a,b,d=c;if(!!(a=/^([\w-]+|%)\(/.exec(j[f]))){a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b,d)}},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)==="u"&&!!s(/^url\(/)){a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)}},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(!(d>57||d<45||d===47))if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==="`"){f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!!t(/^[@\w.%-]+\/[@\w.-]+/)&&(a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i==="."||i==="#"){while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)}},definition:function(){var a,d=[],f,g,h,i;if(!(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/)))if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!!s(/^\(opacity=/i))if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=s(this.combinator),a=s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/);if(a)return new e.Element(c,a)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="&"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!!s("[")){if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"}},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,g;p();if(g=/^([.#: \w-]+)[\s\n]*\{/.exec(j[f]))c+=g[0].length-1,a=[new e.Selector([new e.Element(null,g[1])])];else while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g!=="."&&g!=="#"&&g!=="&")if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)==="@"){if(d=s(this["import"]))return d;if(a=s(/^@media|@page|@-[-a-z]+/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)}},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}},typeof a!="undefined"&&(d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)}),function(a){function d(a){return Math.min(1,Math.max(0,a))}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){a=a<0?a+1:a>1?a-1:a;return a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();e.s+=c.value/100,e.s=d(e.s);return b(e)},desaturate:function(a,c){var e=a.toHSL();e.s-=c.value/100,e.s=d(e.s);return b(e)},lighten:function(a,c){var e=a.toHSL();e.l+=c.value/100,e.l=d(e.l);return b(e)},darken:function(a,c){var e=a.toHSL();e.l-=c.value/100,e.l=d(e.l);return b(e)},fadein:function(a,c){var e=a.toHSL();e.a+=c.value/100,e.a=d(e.a);return b(e)},fadeout:function(a,c){var e=a.toHSL();e.a-=c.value/100,e.a=d(e.a);return b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;d.h=e<0?360+e:e;return b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16);return a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b":a.compress?">":" > "}[this.value]}}(c("less/tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("less/tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;e1?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}});return this._lookups[g]=d},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;if(!this.root)if(b.length===0)g=this.selectors.map(function(a){return[a]});else for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f);return d.join("")+(c.compress?"\n":"")}}}(c("less/tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){return this.elements[0].value===a.elements[0].value?!0:!1},a.Selector.prototype.toCSS=function(a){if(this._css)return this._css;return this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("less/tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(!/^(?:https?:\/|file:\/|data:\/)?\//.test(b.value)&&c.length>0&&typeof a!="undefined"&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("less/tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("less/tree")),function(a){a.Variable=function(a,b){this.name=a,this -.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("less/tree")),c("less/tree").find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c){a&&q(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof(window) === 'undefined') { - less = exports, - tree = require('less/tree'); -} else { - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/) || $(/^(?:\d*\.)?\d+%/); - - if (e) { return new(tree.Element)(c, e) } - - if (c.value && c.value[0] === '&') { - return new(tree.Element)(c, null); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - if (match = /^([.#:% \w-]+)[\s\n]*\{/.exec(chunks[j])) { - i += match[0].length - 1; - selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])]; - } else { - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-)?keyframes/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (typeof(window) !== 'undefined') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('less/tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { message: "error evaluating function `" + this.name + "`", - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else if (rgb.length == 8) { - this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0; - this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('less/tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Element = function (combinator, value) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value ? value.trim() : ""; -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('less/tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('less/tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('less/tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors( paths, context, this.selectors ); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value[0] === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('less/tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - if (this.elements[0].value === other.elements[0].value) { - return true; - } else { - return false; - } -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('less/tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('less/tree')); -require('less/tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('less/tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - styles[i].type = 'text/css'; - styles[i].innerHTML = tree.toCSS(); - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.4.min.js b/dist/less-1.1.4.min.js deleted file mode 100644 index 182b526fb..000000000 --- a/dist/less-1.1.4.min.js +++ /dev/null @@ -1,16 +0,0 @@ -// -// LESS - Leaner CSS v1.1.4 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -// -// LESS - Leaner CSS v1.1.4 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function u(a,b){var c="less-error-message:"+o(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,p([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}function t(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function s(a){return a&&a.parentNode.removeChild(a)}function r(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){t("browser doesn't support AJAX.");return null}}function q(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var g=r(),h=f?!1:d.async;typeof g.overrideMimeType=="function"&&g.overrideMimeType("text/css"),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),f?g.status===0?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){g.readyState==4&&i(g,c,e)}:i(g,c,e)}function p(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||o(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(h){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&g&&(t("saving "+e+" to cache."),g.setItem(e,a),g.setItem(e+":timestamp",c))}function o(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function n(b,c,e,f){var h=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=g&&g.getItem(i),k=g&&g.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=h.slice(0,h.lastIndexOf("/")+1)+i),q(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())p(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type})).parse(a,function(a,d){if(a)return u(a,i);try{c(d,b,{local:!1,lastModified:g,remaining:f}),s(document.getElementById("less-error-message:"+o(i)))}catch(a){u(a,i)}})}catch(h){u(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function m(a,b){for(var c=0;c>>0;for(var d=0;d>>0,c=Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;ck&&(j[f]=j[f].slice(c-k),k=c)}function q(){j[f]=g,c=h,k=c}function p(){g=j[f],h=c,k=c}var b,c,f,g,h,i,j,k,l,m=this,n=function(){},o=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=a,c(a),e.queue.length===0&&n()},a)}};this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null;return l={imports:o,parse:function(d,g){var h,l,m,o,p,q,r=[],t,u=null;c=f=k=i=0,j=[],b=d.replace(/\r\n/g,"\n"),j=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b]));return new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)==="/"){if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)}},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==='"'||b.charAt(d)==="'"){f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)}},keyword:function(){var a;if(a=s(/^[A-Za-z-]+/))return new e.Keyword(a)},call:function(){var a,b,d=c;if(!!(a=/^([\w-]+|%)\(/.exec(j[f]))){a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b,d)}},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)==="u"&&!!s(/^url\(/)){a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)}},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(!(d>57||d<45||d===47))if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)==="`"){f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!!t(/^[@\w.%-]+\/[@\w.-]+/)&&(a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i==="."||i==="#"){while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)}},definition:function(){var a,d=[],f,g,h,i;if(!(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/)))if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!!s(/^\(opacity=/i))if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=s(this.combinator),a=s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/)||s(/^(?:\d*\.)?\d+%/);if(a)return new e.Element(c,a);if(c.value&&c.value[0]==="&")return new e.Element(c,null)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d==="&"){a="&",c++,b.charAt(c)===" "&&(a="& ");while(b.charAt(c)===" ")c++;return new e.Combinator(a)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!!s("[")){if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"}},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,g;p();if(g=/^([.#:% \w-]+)[\s\n]*\{/.exec(j[f]))c+=g[0].length-1,a=[new e.Selector([new e.Element(null,g[1])])];else while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g!=="."&&g!=="#"&&g!=="&")if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)==="@"){if(d=s(this["import"]))return d;if(a=s(/^@media|@page/)||s(/^@(?:-webkit-)?keyframes/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)}},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}},typeof a!="undefined"&&(d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),n({href:a,title:a,type:d.mime},c,!0)}),function(a){function d(a){return Math.min(1,Math.max(0,a))}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){a=a<0?a+1:a>1?a-1:a;return a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();e.s+=c.value/100,e.s=d(e.s);return b(e)},desaturate:function(a,c){var e=a.toHSL();e.s-=c.value/100,e.s=d(e.s);return b(e)},lighten:function(a,c){var e=a.toHSL();e.l+=c.value/100,e.l=d(e.l);return b(e)},darken:function(a,c){var e=a.toHSL();e.l-=c.value/100,e.l=d(e.l);return b(e)},fadein:function(a,c){var e=a.toHSL();e.a+=c.value/100,e.a=d(e.a);return b(e)},fadeout:function(a,c){var e=a.toHSL();e.a-=c.value/100,e.a=d(e.a);return b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;d.h=e<0?360+e:e;return b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16);return a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b":a.compress?">":" > "}[this.value]}}(c("less/tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("less/tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;e1?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}});return this._lookups[g]=d},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f);return d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&typeof a!="undefined"&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs -.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("less/tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("less/tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("less/tree")),c("less/tree").find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var f=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||f?"development":"production"),d.async=!1,d.poll=d.poll||(f?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&m(function(a,b,c){a&&p(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var g;try{g=typeof a.localStorage=="undefined"?null:a.localStorage}catch(h){g=null}var i=document.getElementsByTagName("link"),j=/^text\/(x-)?less$/;d.sheets=[];for(var k=0;k>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - less = {}; - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-|-moz-)?keyframes/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { message: "error evaluating function `" + this.name + "`", - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value ? value.trim() : ""; - this.index = index; -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors( paths, context, this.selectors ); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (less.mode === 'browser' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('../tree')); -require('./tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('./tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - try { - style.innerHTML = css; - } catch (_) { - style.styleSheets.cssText = css; - } - style.type = 'text/css'; - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.5.min.js b/dist/less-1.1.5.min.js deleted file mode 100644 index 49949fbea..000000000 --- a/dist/less-1.1.5.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.1.5 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function c(b){return a.less[b.split("/")[1]]}function l(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&g&&(t("saving "+e+" to cache."),g.setItem(e,a),g.setItem(e+":timestamp",c))}function q(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var g=r(),h=f?!1:d.async;typeof g.overrideMimeType=="function"&&g.overrideMimeType("text/css"),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),f?g.status===0?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){g.readyState==4&&i(g,c,e)}:i(g,c,e)}function r(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return t("browser doesn't support AJAX."),null}}function s(a){return a&&a.parentNode.removeChild(a)}function t(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function u(a,b){var c="less-error-message:"+o(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,p([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;ck&&(j[f]=j[f].slice(c-k),k=c)}function s(a){var d,e,g,h,i,m,n,o;if(a instanceof Function)return a.call(l.parsers);if(typeof a=="string")d=b.charAt(c)===a?a:null,g=1,r();else{r();if(d=a.exec(j[f]))g=d[0].length;else return null}if(d){o=c+=g,m=c+j[f].length-g;while(c0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),h=new e.Ruleset([],s(this.parsers.primary)),h.root=!0,h.toCSS=function(c){var d,f,g;return function(g,h){function n(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var i=[];g=g||{},typeof h=="object"&&!Array.isArray(h)&&(h=Object.keys(h).map(function(a){var b=h[a];return b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b])),new e.Rule("@"+a,b,!1,0)}),i=[new e.Ruleset(null,h)]);try{var j=c.call(this,{frames:i}).toCSS([],{compress:g.compress||!1})}catch(k){f=b.split("\n"),d=n(k.index);for(var l=k.index,m=-1;l>=0&&b.charAt(l)!=="\n";l--)m++;throw{type:k.type,message:k.message,filename:a.filename,index:k.index,line:typeof d=="number"?d+1:null,callLine:k.call&&n(k.call)+1,callExtract:f[n(k.call)],stack:k.stack,column:m,extract:[f[d-1],f[d],f[d+1]]}}return g.compress?j.replace(/(\s)+/g,"$1"):j}}(h.eval);if(c=0&&b.charAt(v)!=="\n";v--)w++;u={name:"ParseError",message:"Syntax Error on line "+p,index:c,filename:a.filename,line:p,column:w,extract:[q[p-2],q[p-1],q[p]]}}this.imports.queue.length>0?n=function(){g(u,h)}:g(u,h)},parsers:{primary:function(){var a,b=[];while((a=s(this.mixin.definition)||s(this.rule)||s(this.ruleset)||s(this.mixin.call)||s(this.comment)||s(this.directive))||s(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(c)!=="/")return;if(b.charAt(c+1)==="/")return new e.Comment(s(/^\/\/.*/),!0);if(a=s(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)},entities:{quoted:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)!=='"'&&b.charAt(d)!=="'")return;f&&s("~");if(a=s(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],f)},keyword:function(){var a;if(a=s(/^[_A-Za-z-][_A-Za-z0-9-]*/))return new e.Keyword(a)},call:function(){var a,b,d=c;if(!(a=/^([\w-]+|%)\(/.exec(j[f])))return;a=a[1].toLowerCase();if(a==="url")return null;c+=a.length;if(a==="alpha")return s(this.alpha);s("("),b=s(this.entities.arguments);if(!s(")"))return;if(a)return new e.Call(a,b,d)},arguments:function(){var a=[],b;while(b=s(this.expression)){a.push(b);if(!s(","))break}return a},literal:function(){return s(this.entities.dimension)||s(this.entities.color)||s(this.entities.quoted)},url:function(){var a;if(b.charAt(c)!=="u"||!s(/^url\(/))return;a=s(this.entities.quoted)||s(this.entities.variable)||s(this.entities.dataURI)||s(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!s(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),o.paths)},dataURI:function(){var a;if(s(/^data:/)){a={},a.mime=s(/^[^\/]+\/[^,;)]+/)||"",a.charset=s(/^;\s*charset=[^,;)]+/)||"",a.base64=s(/^;\s*base64/)||"",a.data=s(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,d=c;if(b.charAt(c)==="@"&&(a=s(/^@@?[\w-]+/)))return new e.Variable(a,d)},color:function(){var a;if(b.charAt(c)==="#"&&(a=s(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,d=b.charCodeAt(c);if(d>57||d<45||d===47)return;if(a=s(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,d=c,f;b.charAt(d)==="~"&&(d++,f=!0);if(b.charAt(d)!=="`")return;f&&s("~");if(a=s(/^`([^`]*)`/))return new e.JavaScript(a[1],c,f)}},variable:function(){var a;if(b.charAt(c)==="@"&&(a=s(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!t(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=s(this.entity))&&s("/")&&(b=s(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],d,f,g,h=c,i=b.charAt(c);if(i!=="."&&i!=="#")return;while(d=s(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(f,d,c)),f=s(">");s("(")&&(g=s(this.entities.arguments))&&s(")");if(a.length>0&&(s(";")||t("}")))return new e.mixin.Call(a,g,h)},definition:function(){var a,d=[],f,g,h,i;if(b.charAt(c)!=="."&&b.charAt(c)!=="#"||t(/^[^{]*(;|})/))return;if(f=s(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=f[1];while(h=s(this.entities.variable)||s(this.entities.literal)||s(this.entities.keyword)){if(h instanceof e.Variable)if(s(":"))if(i=s(this.expression))d.push({name:h.name,value:i});else throw new Error("Expected value");else d.push({name:h.name});else d.push({value:h});if(!s(","))break}if(!s(")"))throw new Error("Expected )");g=s(this.block);if(g)return new e.mixin.Definition(a,d,g)}}},entity:function(){return s(this.entities.literal)||s(this.entities.variable)||s(this.entities.url)||s(this.entities.call)||s(this.entities.keyword)||s(this.entities.javascript)||s(this.comment)},end:function(){return s(";")||t("}")},alpha:function(){var a;if(!s(/^\(opacity=/i))return;if(a=s(/^\d+/)||s(this.entities.variable)){if(!s(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,d;d=s(this.combinator),a=s(/^(?:\d+\.\d+|\d+)%/)||s(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||s("*")||s(this.attribute)||s(/^\([^)@]+\)/);if(a)return new e.Element(d,a,c);if(d.value&&d.value.charAt(0)==="&")return new e.Element(d,null,c)},combinator:function(){var a,d=b.charAt(c);if(d===">"||d==="+"||d==="~"){c++;while(b.charAt(c)===" ")c++;return new e.Combinator(d)}if(d==="&"){a="&",c++,b.charAt(c)===" "&&(a="& ");while(b.charAt(c)===" ")c++;return new e.Combinator(a)}if(d===":"&&b.charAt(c+1)===":"){c+=2;while(b.charAt(c)===" ")c++;return new e.Combinator("::")}return b.charAt(c-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,d,f=[],g,h;while(d=s(this.element)){g=b.charAt(c),f.push(d);if(g==="{"||g==="}"||g===";"||g===",")break}if(f.length>0)return new e.Selector(f)},tag:function(){return s(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||s("*")},attribute:function(){var a="",b,c,d;if(!s("["))return;if(b=s(/^[a-zA-Z-]+/)||s(this.entities.quoted))(d=s(/^[|~*$^]?=/))&&(c=s(this.entities.quoted)||s(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!s("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(s("{")&&(a=s(this.primary))&&s("}"))return a},ruleset:function(){var a=[],b,d,f;p();while(b=s(this.selector)){a.push(b),s(this.comment);if(!s(","))break;s(this.comment)}if(a.length>0&&(d=s(this.block)))return new e.Ruleset(a,d);i=c,q()},rule:function(){var a,d,g=b.charAt(c),k,l;p();if(g==="."||g==="#"||g==="&")return;if(a=s(this.variable)||s(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(j[f]))?(c+=l[0].length-1,d=new e.Anonymous(l[1])):a==="font"?d=s(this.font):d=s(this.value),k=s(this.important);if(d&&s(this.end))return new e.Rule(a,d,k,h);i=c,q()}},"import":function(){var a;if(s(/^@import\s+/)&&(a=s(this.entities.quoted)||s(this.entities.url))&&s(";"))return new e.Import(a,o)},directive:function(){var a,d,f,g;if(b.charAt(c)!=="@")return;if(d=s(this["import"]))return d;if(a=s(/^@media|@page/)||s(/^@(?:-webkit-|-moz-)?keyframes/)){g=(s(/^[^{]+/)||"").trim();if(f=s(this.block))return new e.Directive(a+" "+g,f)}else if(a=s(/^@[-a-z]+/))if(a==="@font-face"){if(f=s(this.block))return new e.Directive(a,f)}else if((d=s(this.entity))&&s(";"))return new e.Directive(a,d)},font:function(){var a=[],b=[],c,d,f,g;while(g=s(this.shorthand)||s(this.entity))b.push(g);a.push(new e.Expression(b));if(s(","))while(g=s(this.expression)){a.push(g);if(!s(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=s(this.expression)){b.push(a);if(!s(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(c)==="!")return s(/^! *important/)},sub:function(){var a;if(s("(")&&(a=s(this.expression))&&s(")"))return a},multiplication:function(){var a,b,c,d;if(a=s(this.operand)){while((c=s("/")||s("*"))&&(b=s(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,d,f,g;if(a=s(this.multiplication)){while((f=s(/^[-+]\s+/)||b.charAt(c-1)!=" "&&(s("+")||s("-")))&&(d=s(this.multiplication)))g=new e.Operation(f,[g||a,d]);return g||a}},operand:function(){var a,d=b.charAt(c+1);b.charAt(c)==="-"&&(d==="@"||d==="(")&&(a=s("-"));var f=s(this.sub)||s(this.entities.dimension)||s(this.entities.color)||s(this.entities.variable)||s(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),f]):f},expression:function(){var a,b,c=[],d;while(a=s(this.addition)||s(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=s(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),n({href:a,title:a,type:d.mime},c,!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)}}}(c("../tree")),function(a){a.Directive=function(b,c){this.name=b,Array.isArray(c)?this.ruleset=new a.Ruleset([],c):this.value=c},a.Directive.prototype={toCSS:function(a,b){return this.ruleset?(this.ruleset.root=!0,this.name+(b.compress?"{":" {\n ")+this.ruleset.toCSS(a,b).trim().replace(/\n/g,"\n ")+(b.compress?"}":"\n}\n")):this.name+" "+this.value.toCSS()+";\n"},eval:function(a){return a.frames.unshift(this),this.ruleset=this.ruleset&&this.ruleset.eval(a),a.frames.shift(),this},variable:function(b){return a.Ruleset.prototype.variable.call(this.ruleset,b)},find:function(){return a.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.ruleset)}}}(c("../tree")),function(a){a.Element=function(b,c,d){this.combinator=b instanceof a.Combinator?b:new a.Combinator(b),this.value=c?c.trim():"",this.index=d},a.Element.prototype.toCSS=function(a){return this.combinator.toCSS(a||{})+this.value},a.Combinator=function(a){a===" "?this.value=" ":a==="& "?this.value="& ":this.value=a?a.trim():""},a.Combinator.prototype.toCSS=function(a){return{"":""," ":" ","&":"","& ":" ",":":" :","::":"::","+":a.compress?"+":" + ","~":a.compress?"~":" ~ ",">":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;ee.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(a.value=b[0]+(a.value.charAt(0)==="/"?a.value.slice(1):a.value)),this.value=a,this.paths=b)},a.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(b){return this.attrs?this:new a.URL(this.value.eval(b),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("../tree")),c("./tree").find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var f=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||f?"development":"production"),d.async=!1,d.poll=d.poll||(f?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&m(function(a,b,c){a&&p(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var g;try{g=typeof a.localStorage=="undefined"?null:a.localStorage}catch(h){g=null}var i=document.getElementsByTagName("link"),j=/^text\/(x-)?less$/;d.sheets=[];for(var k=0;k>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - less = {}; - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k) - } - } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/) || $('keyframes')) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - return this._math('round', n); - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { message: "error evaluating function `" + this.name + "`", - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value ? value.trim() : ""; - this.index = index; -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors( paths, context, this.selectors ); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('../tree')); -require('./tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('./tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - try { - style.innerHTML = css; - } catch (_) { - style.styleSheets.cssText = css; - } - style.type = 'text/css'; - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.1.6.min.js b/dist/less-1.1.6.min.js deleted file mode 100644 index a30623b99..000000000 --- a/dist/less-1.1.6.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.1.6 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function c(b){return a.less[b.split("/")[1]]}function l(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&g&&(t("saving "+e+" to cache."),g.setItem(e,a),g.setItem(e+":timestamp",c))}function q(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var g=r(),h=f?!1:d.async;typeof g.overrideMimeType=="function"&&g.overrideMimeType("text/css"),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),f?g.status===0||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){g.readyState==4&&i(g,c,e)}:i(g,c,e)}function r(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return t("browser doesn't support AJAX."),null}}function s(a){return a&&a.parentNode.removeChild(a)}function t(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function u(a,b){var c="less-error-message:"+o(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,p([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;cl&&(k[g]=k[g].slice(f-l),l=f)}function t(a){var c,d,e,h,i,j,n,o;if(a instanceof Function)return a.call(m.parsers);if(typeof a=="string")c=b.charAt(f)===a?a:null,e=1,s();else{s();if(c=a.exec(k[g]))e=c[0].length;else return null}if(c){o=f+=e,j=f+k[g].length-e;while(f0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]),m=new e.Ruleset([],t(this.parsers.primary)),m.root=!0,m.toCSS=function(f){var g,h,i;return function(i,j){function p(a){return a?(b.slice(0,a).match(/\n/g)||"").length:null}var k=[];i=i||{},typeof j=="object"&&!Array.isArray(j)&&(j=Object.keys(j).map(function(a){var b=j[a];return b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b])),new e.Rule("@"+a,b,!1,0)}),k=[new e.Ruleset(null,j)]);try{var l=f.call(this,{frames:k}).toCSS([],{compress:i.compress||!1})}catch(m){h=b.split("\n"),g=p(m.index);for(var n=m.index,o=-1;n>=0&&b.charAt(n)!=="\n";n--)o++;throw{type:m.type,message:m.message,filename:a.filename,index:m.index,line:typeof g=="number"?g+1:null,callLine:m.call&&p(m.call)+1,callExtract:h[p(m.call)],stack:m.stack,column:o,extract:[h[g-1],h[g],h[g+1]]}}return i.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(l):i.compress?l.replace(/(\s)+/g,"$1"):l}}(m.eval);if(f=0&&b.charAt(x)!=="\n";x--)y++;w={name:"ParseError",message:"Syntax Error on line "+r,index:f,filename:a.filename,line:r,column:y,extract:[s[r-2],s[r-1],s[r]]}}this.imports.queue.length>0?o=function(){i(w,m)}:i(w,m)},parsers:{primary:function(){var a,b=[];while((a=t(this.mixin.definition)||t(this.rule)||t(this.ruleset)||t(this.mixin.call)||t(this.comment)||t(this.directive))||t(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(f)!=="/")return;if(b.charAt(f+1)==="/")return new e.Comment(t(/^\/\/.*/),!0);if(a=t(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)},entities:{quoted:function(){var a,c=f,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=='"'&&b.charAt(c)!=="'")return;d&&t("~");if(a=t(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],d)},keyword:function(){var a;if(a=t(/^[_A-Za-z-][_A-Za-z0-9-]*/))return e.colors.hasOwnProperty(a)?new e.Color(e.colors[a].slice(1)):new e.Keyword(a)},call:function(){var a,b,c=f;if(!(a=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(k[g])))return;a=a[1].toLowerCase();if(a==="url")return null;f+=a.length;if(a==="alpha")return t(this.alpha);t("("),b=t(this.entities.arguments);if(!t(")"))return;if(a)return new e.Call(a,b,c)},arguments:function(){var a=[],b;while(b=t(this.entities.assignment)||t(this.expression)){a.push(b);if(!t(","))break}return a},literal:function(){return t(this.entities.dimension)||t(this.entities.color)||t(this.entities.quoted)},assignment:function(){var a,b;if((a=t(/^\w+(?=\s?=)/i))&&t("=")&&(b=t(this.entity)))return new e.Assignment(a,b)},url:function(){var a;if(b.charAt(f)!=="u"||!t(/^url\(/))return;a=t(this.entities.quoted)||t(this.entities.variable)||t(this.entities.dataURI)||t(/^[-\w%@$\/.&=:;#+?~]+/)||"";if(!t(")"))throw new Error("missing closing ) for url()");return new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),p.paths)},dataURI:function(){var a;if(t(/^data:/)){a={},a.mime=t(/^[^\/]+\/[^,;)]+/)||"",a.charset=t(/^;\s*charset=[^,;)]+/)||"",a.base64=t(/^;\s*base64/)||"",a.data=t(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,c=f;if(b.charAt(f)==="@"&&(a=t(/^@@?[\w-]+/)))return new e.Variable(a,c)},color:function(){var a;if(b.charAt(f)==="#"&&(a=t(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,c=b.charCodeAt(f);if(c>57||c<45||c===47)return;if(a=t(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,c=f,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=="`")return;d&&t("~");if(a=t(/^`([^`]*)`/))return new e.JavaScript(a[1],f,d)}},variable:function(){var a;if(b.charAt(f)==="@"&&(a=t(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!u(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=t(this.entity))&&t("/")&&(b=t(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var a=[],c,d,g,h=f,i=b.charAt(f);if(i!=="."&&i!=="#")return;while(c=t(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new e.Element(d,c,f)),d=t(">");t("(")&&(g=t(this.entities.arguments))&&t(")");if(a.length>0&&(t(";")||u("}")))return new e.mixin.Call(a,g,h)},definition:function(){var a,c=[],d,g,h,i;if(b.charAt(f)!=="."&&b.charAt(f)!=="#"||u(/^[^{]*(;|})/))return;if(d=t(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=d[1];while(h=t(this.entities.variable)||t(this.entities.literal)||t(this.entities.keyword)){if(h instanceof e.Variable)if(t(":"))if(i=t(this.expression))c.push({name:h.name,value:i});else throw new Error("Expected value");else c.push({name:h.name});else c.push({value:h});if(!t(","))break}if(!t(")"))throw new Error("Expected )");g=t(this.block);if(g)return new e.mixin.Definition(a,c,g)}}},entity:function(){return t(this.entities.literal)||t(this.entities.variable)||t(this.entities.url)||t(this.entities.call)||t(this.entities.keyword)||t(this.entities.javascript)||t(this.comment)},end:function(){return t(";")||u("}")},alpha:function(){var a;if(!t(/^\(opacity=/i))return;if(a=t(/^\d+/)||t(this.entities.variable)){if(!t(")"))throw new Error("missing closing ) for alpha()");return new e.Alpha(a)}},element:function(){var a,b,c;c=t(this.combinator),a=t(/^(?:\d+\.\d+|\d+)%/)||t(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||t("*")||t(this.attribute)||t(/^\([^)@]+\)/);if(a)return new e.Element(c,a,f);if(c.value&&c.value.charAt(0)==="&")return new e.Element(c,null,f)},combinator:function(){var a,c=b.charAt(f);if(c===">"||c==="+"||c==="~"){f++;while(b.charAt(f)===" ")f++;return new e.Combinator(c)}if(c==="&"){a="&",f++,b.charAt(f)===" "&&(a="& ");while(b.charAt(f)===" ")f++;return new e.Combinator(a)}if(c===":"&&b.charAt(f+1)===":"){f+=2;while(b.charAt(f)===" ")f++;return new e.Combinator("::")}return b.charAt(f-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,c,d=[],g,h;while(c=t(this.element)){g=b.charAt(f),d.push(c);if(g==="{"||g==="}"||g===";"||g===",")break}if(d.length>0)return new e.Selector(d)},tag:function(){return t(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||t("*")},attribute:function(){var a="",b,c,d;if(!t("["))return;if(b=t(/^[a-zA-Z-]+/)||t(this.entities.quoted))(d=t(/^[|~*$^]?=/))&&(c=t(this.entities.quoted)||t(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!t("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(t("{")&&(a=t(this.primary))&&t("}"))return a},ruleset:function(){var a=[],b,c,d;q();while(b=t(this.selector)){a.push(b),t(this.comment);if(!t(","))break;t(this.comment)}if(a.length>0&&(c=t(this.block)))return new e.Ruleset(a,c);j=f,r()},rule:function(){var a,c,d=b.charAt(f),h,l;q();if(d==="."||d==="#"||d==="&")return;if(a=t(this.variable)||t(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(k[g]))?(f+=l[0].length-1,c=new e.Anonymous(l[1])):a==="font"?c=t(this.font):c=t(this.value),h=t(this.important);if(c&&t(this.end))return new e.Rule(a,c,h,i);j=f,r()}},"import":function(){var a;if(t(/^@import\s+/)&&(a=t(this.entities.quoted)||t(this.entities.url))&&t(";"))return new e.Import(a,p)},directive:function(){var a,c,d,g;if(b.charAt(f)!=="@")return;if(c=t(this["import"]))return c;if(a=t(/^@media|@page/)||t(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)||t("keyframes")){g=(t(/^[^{]+/)||"").trim();if(d=t(this.block))return new e.Directive(a+" "+g,d)}else if(a=t(/^@[-a-z]+/))if(a==="@font-face"){if(d=t(this.block))return new e.Directive(a,d)}else if((c=t(this.entity))&&t(";"))return new e.Directive(a,c)},font:function(){var a=[],b=[],c,d,f,g;while(g=t(this.shorthand)||t(this.entity))b.push(g);a.push(new e.Expression(b));if(t(","))while(g=t(this.expression)){a.push(g);if(!t(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=t(this.expression)){b.push(a);if(!t(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(f)==="!")return t(/^! *important/)},sub:function(){var a;if(t("(")&&(a=t(this.expression))&&t(")"))return a},multiplication:function(){var a,b,c,d;if(a=t(this.operand)){while(!u(/^\/\*/)&&(c=t("/")||t("*"))&&(b=t(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,c,d,g;if(a=t(this.multiplication)){while((d=t(/^[-+]\s+/)||b.charAt(f-1)!=" "&&(t("+")||t("-")))&&(c=t(this.multiplication)))g=new e.Operation(d,[g||a,c]);return g||a}},operand:function(){var a,c=b.charAt(f+1);b.charAt(f)==="-"&&(c==="@"||c==="(")&&(a=t("-"));var d=t(this.sub)||t(this.entities.dimension)||t(this.entities.color)||t(this.entities.variable)||t(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),d]):d},expression:function(){var a,b,c=[],d;while(a=t(this.addition)||t(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=t(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),n({href:a,title:a,type:d.mime},c,!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)}}}(c("../tree")),function(a){a.Directive=function(b,c){this.name=b,Array.isArray(c)?this.ruleset=new a.Ruleset([],c):this.value=c},a.Directive.prototype={toCSS:function(a,b){return this.ruleset?(this.ruleset.root=!0,this.name+(b.compress?"{":" {\n ")+this.ruleset.toCSS(a,b).trim().replace(/\n/g,"\n ")+(b.compress?"}":"\n}\n")):this.name+" "+this.value.toCSS()+";\n"},eval:function(a){return a.frames.unshift(this),this.ruleset=this.ruleset&&this.ruleset.eval(a),a.frames.shift(),this},variable:function(b){return a.Ruleset.prototype.variable.call(this.ruleset,b)},find:function(){return a.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.ruleset)}}}(c("../tree")),function(a){a.Element=function(b,c,d){this.combinator=b instanceof a.Combinator?b:new a.Combinator(b),this.value=c?c.trim():"",this.index=d},a.Element.prototype.toCSS=function(a){return this.combinator.toCSS(a||{})+this.value},a.Combinator=function(a){a===" "?this.value=" ":a==="& "?this.value="& ":this.value=a?a.trim():""},a.Combinator.prototype.toCSS=function(a){return{"":""," ":" ","&":"","& ":" ",":":" :","::":"::","+":a.compress?"+":" + ","~":a.compress?"~":" ~ ",">":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c){var d=this;this._path=b,b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(a){if(!a)throw new Error("Error parsing "+d.path);d.root=a})},a.Import.prototype={toCSS:function(){return this.css?"@import "+this._path.toCSS()+";\n":""},eval:function(b){var c;if(this.css)return this;c=new a.Ruleset(null,this.root.rules.slice(0));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;d=Math.min(c,this.arity);for(var e=0;ee.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("../tree")),c("./tree").find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)};var f=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||f?"development":"production"),d.async=!1,d.poll=d.poll||(f?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&m(function(a,b,c){a&&p(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var g;try{g=typeof a.localStorage=="undefined"?null:a.localStorage}catch(h){g=null}var i=document.getElementsByTagName("link"),j=/^text\/(x-)?less$/;d.sheets=[];for(var k=0;k>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - if (e && !that.error) { that.error = e } - callback(e, root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getLocation(index) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: index ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function LessError(e, env) { - var lines = input.split('\n'), - loc = getLocation(e.index), - line = loc.line, - col = loc.column; - - this.type = e.type || 'SyntaxError'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call) + 1); - this.callExtract = lines[getLocation(e.call)]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - throw new(LessError)(e, env); - } - - if (parser.imports.error) { throw parser.imports.error } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, important); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - expect(')'); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (! e) { - $('(') && (v = $(this.entities.variable)) && $(')') && (e = new(tree.Paren)(v)); - } - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features); - } - } - }, - - mediaFeature: function () { - var nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var f, features = []; - while (f = $(this.mediaFeature)) { - features.push(f); - if (! $(',')) { break } - } - return features.length > 0 ? features : null; - }, - - media: function () { - var features; - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - return new(tree.Directive)('@media', rules, features); - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types, e, nodes; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - return this._math('round', n); - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value, features) { - this.name = name; - this.features = features && new(tree.Value)(features); - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.ruleset) { - this.ruleset.root = true; - return this.name + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - this.features = this.features && this.features.eval(env); - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value); -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features) { - var that = this; - - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root) { - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Directive)('@media', ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []); - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0)); - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors(paths, context, this.selectors); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (root, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (root, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - - var template = ['
    ', - '
  • {0}
  • ', - '
  • {current}
  • ', - '
  • {2}
  • ', - '
'].join('\n'); - - var elem = document.createElement('div'), timer, content; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

' + (e.message || 'There is an error in your .less file') + - '

' + '

' + href + " "; - - if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + - template.replace(/\[(-?\d)\]/g, function (_, i) { - return (parseInt(e.line) + parseInt(i)) || ''; - }).replace(/\{(\d)\}/g, function (_, i) { - return e.extract[parseInt(i)] || ''; - }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '' + - e.extract[1].slice(e.column) + ''); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #ee4444;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.ctx {', - 'color: #dd4444;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.2.0.min.js b/dist/less-1.2.0.min.js deleted file mode 100644 index 84e90475a..000000000 --- a/dist/less-1.2.0.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.2.0 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function c(b){return a.less[b.split("/")[1]]}function m(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(v("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function s(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=t(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0||f.status>=200&&f.status<300?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function t(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return v("browser doesn't support AJAX."),null}}function u(a){return a&&a.parentNode.removeChild(a)}function v(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function w(a,b){var c="less-error-message:"+q(b),e=["
    ",'
  • {0}
  • ',"
  • {current}
  • ",'
  • {2}
  • ',"
"].join("\n"),f=document.createElement("div"),g,h;f.id=c,f.className="less-error-message",h="

"+(a.message||"There is an error in your .less file")+"

"+'

'+b+" ",a.extract&&(h+="on line "+a.line+", column "+(a.column+1)+":

"+e.replace(/\[(-?\d)\]/g,function(b,c){return parseInt(a.line)+parseInt(c)||""}).replace(/\{(\d)\}/g,function(b,c){return a.extract[parseInt(c)]||""}).replace(/\{current\}/,a.extract[1].slice(0,a.column)+''+a.extract[1].slice(a.column)+"")),f.innerHTML=h,r([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #ee4444;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.ctx {","color: #dd4444;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;cm&&(l[h]=l[h].slice(g-m),m=g)}function v(a){var c,d,e,f,i,j,k,o;if(a instanceof Function)return a.call(n.parsers);if(typeof a=="string")c=b.charAt(g)===a?a:null,e=1,u();else{u();if(c=a.exec(l[h]))e=c[0].length;else return null}if(c){o=g+=e,j=g+l[h].length-e;while(g=0&&b.charAt(c)!=="\n";c--)d++;return{line:a?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function A(a,c){var d=b.split("\n"),e=z(a.index),f=e.line,g=e.column;this.type=a.type||"SyntaxError",this.message=a.message,this.filename=a.filename||c.filename,this.index=a.index,this.line=typeof f=="number"?f+1:null,this.callLine=a.call&&z(a.call)+1,this.callExtract=d[z(a.call)],this.stack=a.stack,this.column=g,this.extract=[d[f-1],d[f],d[f+1]]}var b,g,h,i,j,k,l,m,n,o=this,q=function(){},r=this.imports={paths:a&&a.paths||[],queue:[],files:{},mime:a&&a.mime,error:null,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a,d){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=d,a&&!e.error&&(e.error=a),c(a,d),e.queue.length===0&&q()},a)}};return this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,n={imports:r,parse:function(e,i){var j,o,p,r,s,t,u=[],w,x=null;g=h=m=k=0,l=[],b=e.replace(/\r\n/g,"\n"),l=function(c){var d=0,e=/[^"'`\{\}\/\(\)]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=0,h,i=c[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:a.filename};return c.map(function(a){return a.join("")})}([[]]);try{j=new f.Ruleset([],v(this.parsers.primary)),j.root=!0}catch(y){return i(new A(y,a))}j.toCSS=function(b){var e,g,h;return function(e,g){var h=[];e=e||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof f.Value||(b instanceof f.Expression||(b=new f.Expression([b])),b=new f.Value([b])),new f.Rule("@"+a,b,!1,0)}),h=[new f.Ruleset(null,g)]);try{var i=b.call(this,{frames:h}).toCSS([],{compress:e.compress||!1})}catch(j){throw new A(j,a)}if(n.imports.error)throw n.imports.error;return e.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(i):e.compress?i.replace(/(\s)+/g,"$1"):i}}(j.eval);if(g=0&&b.charAt(z)!=="\n";z--)B++;x={type:"Parse",message:"Syntax Error on line "+s,index:g,filename:a.filename,line:s,column:B,extract:[t[s-2],t[s-1],t[s]]}}this.imports.queue.length>0?q=function(){i(x,j)}:i(x,j)},parsers:{primary:function(){var a,b=[];while((a=v(this.mixin.definition)||v(this.rule)||v(this.ruleset)||v(this.mixin.call)||v(this.comment)||v(this.directive))||v(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(g)!=="/")return;if(b.charAt(g+1)==="/")return new f.Comment(v(/^\/\/.*/),!0);if(a=v(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new f.Comment(a)},entities:{quoted:function(){var a,c=g,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=='"'&&b.charAt(c)!=="'")return;d&&v("~");if(a=v(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new f.Quoted(a[0],a[1]||a[2],d)},keyword:function(){var a;if(a=v(/^[_A-Za-z-][_A-Za-z0-9-]*/))return f.colors.hasOwnProperty(a)?new f.Color(f.colors[a].slice(1)):new f.Keyword(a)},call:function(){var a,b,c=g;if(!(a=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(l[h])))return;a=a[1].toLowerCase();if(a==="url")return null;g+=a.length;if(a==="alpha")return v(this.alpha);v("("),b=v(this.entities.arguments);if(!v(")"))return;if(a)return new f.Call(a,b,c)},arguments:function(){var a=[],b;while(b=v(this.entities.assignment)||v(this.expression)){a.push(b);if(!v(","))break}return a},literal:function(){return v(this.entities.dimension)||v(this.entities.color)||v(this.entities.quoted)},assignment:function(){var a,b;if((a=v(/^\w+(?=\s?=)/i))&&v("=")&&(b=v(this.entity)))return new f.Assignment(a,b)},url:function(){var a;if(b.charAt(g)!=="u"||!v(/^url\(/))return;return a=v(this.entities.quoted)||v(this.entities.variable)||v(this.entities.dataURI)||v(/^[-\w%@$\/.&=:;#+?~]+/)||"",w(")"),new f.URL(a.value||a.data||a instanceof f.Variable?a:new f.Anonymous(a),r.paths)},dataURI:function(){var a;if(v(/^data:/)){a={},a.mime=v(/^[^\/]+\/[^,;)]+/)||"",a.charset=v(/^;\s*charset=[^,;)]+/)||"",a.base64=v(/^;\s*base64/)||"",a.data=v(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,c=g;if(b.charAt(g)==="@"&&(a=v(/^@@?[\w-]+/)))return new f.Variable(a,c)},color:function(){var a;if(b.charAt(g)==="#"&&(a=v(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new f.Color(a[1])},dimension:function(){var a,c=b.charCodeAt(g);if(c>57||c<45||c===47)return;if(a=v(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new f.Dimension(a[1],a[2])},javascript:function(){var a,c=g,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=="`")return;d&&v("~");if(a=v(/^`([^`]*)`/))return new f.JavaScript(a[1],g,d)}},variable:function(){var a;if(b.charAt(g)==="@"&&(a=v(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!y(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=v(this.entity))&&v("/")&&(b=v(this.entity)))return new f.Shorthand(a,b)},mixin:{call:function(){var a=[],c,d,e,h=g,i=b.charAt(g),j=!1;if(i!=="."&&i!=="#")return;while(c=v(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new f.Element(d,c,g)),d=v(">");v("(")&&(e=v(this.entities.arguments))&&v(")"),v(this.important)&&(j=!0);if(a.length>0&&(v(";")||y("}")))return new f.mixin.Call(a,e,h,j)},definition:function(){var a,c=[],d,e,h,i,j;if(b.charAt(g)!=="."&&b.charAt(g)!=="#"||y(/^[^{]*(;|})/))return;s();if(d=v(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=d[1];while(h=v(this.entities.variable)||v(this.entities.literal)||v(this.entities.keyword)){h instanceof f.Variable?v(":")?(i=w(this.expression,"expected expression"),c.push({name:h.name,value:i})):c.push({name:h.name}):c.push({value:h});if(!v(","))break}w(")"),v(/^when/)&&(j=w(this.conditions,"expected condition")),e=v(this.block);if(e)return new f.mixin.Definition(a,c,e,j);t()}}},entity:function(){return v(this.entities.literal)||v(this.entities.variable)||v(this.entities.url)||v(this.entities.call)||v(this.entities.keyword)||v(this.entities.javascript)||v(this.comment)},end:function(){return v(";")||y("}")},alpha:function(){var a;if(!v(/^\(opacity=/i))return;if(a=v(/^\d+/)||v(this.entities.variable))return w(")"),new f.Alpha(a)},element:function(){var a,b,c,d;c=v(this.combinator),a=v(/^(?:\d+\.\d+|\d+)%/)||v(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||v("*")||v(this.attribute)||v(/^\([^)@]+\)/),a||v("(")&&(d=v(this.entities.variable))&&v(")")&&(a=new f.Paren(d));if(a)return new f.Element(c,a,g);if(c.value&&c.value.charAt(0)==="&")return new f.Element(c,null,g)},combinator:function(){var a,c=b.charAt(g);if(c===">"||c==="+"||c==="~"){g++;while(b.charAt(g)===" ")g++;return new f.Combinator(c)}if(c==="&"){a="&",g++,b.charAt(g)===" "&&(a="& ");while(b.charAt(g)===" ")g++;return new f.Combinator(a)}if(c===":"&&b.charAt(g+1)===":"){g+=2;while(b.charAt(g)===" ")g++;return new f.Combinator("::")}return b.charAt(g-1)===" "?new f.Combinator(" "):new f.Combinator(null)},selector:function(){var a,c,d=[],e,h;while(c=v(this.element)){e=b.charAt(g),d.push(c);if(e==="{"||e==="}"||e===";"||e===",")break}if(d.length>0)return new f.Selector(d)},tag:function(){return v(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||v("*")},attribute:function(){var a="",b,c,d;if(!v("["))return;if(b=v(/^[a-zA-Z-]+/)||v(this.entities.quoted))(d=v(/^[|~*$^]?=/))&&(c=v(this.entities.quoted)||v(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!v("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(v("{")&&(a=v(this.primary))&&v("}"))return a},ruleset:function(){var a=[],b,c,d;s();while(b=v(this.selector)){a.push(b),v(this.comment);if(!v(","))break;v(this.comment)}if(a.length>0&&(c=v(this.block)))return new f.Ruleset(a,c);k=g,t()},rule:function(){var a,c,d=b.charAt(g),e,i;s();if(d==="."||d==="#"||d==="&")return;if(a=v(this.variable)||v(this.property)){a.charAt(0)!="@"&&(i=/^([^@+\/'"*`(;{}-]*);/.exec(l[h]))?(g+=i[0].length-1,c=new f.Anonymous(i[1])):a==="font"?c=v(this.font):c=v(this.value),e=v(this.important);if(c&&v(this.end))return new f.Rule(a,c,e,j);k=g,t()}},"import":function(){var a,b;if(v(/^@import\s+/)&&(a=v(this.entities.quoted)||v(this.entities.url))){b=v(this.mediaFeatures);if(v(";"))return new f.Import(a,r,b)}},mediaFeature:function(){var a=[];do if(e=v(this.entities.keyword))a.push(e);else if(v("(")){p=v(this.property),e=v(this.entity);if(!v(")"))return null;if(p&&e)a.push(new f.Paren(new f.Rule(p,e,null,g,!0)));else if(e)a.push(new f.Paren(e));else return null}while(e);if(a.length>0)return new f.Expression(a)},mediaFeatures:function(){var a,b=[];while(a=v(this.mediaFeature)){b.push(a);if(!v(","))break}return b.length>0?b:null},media:function(){var a;if(v(/^@media/)){a=v(this.mediaFeatures);if(rules=v(this.block))return new f.Directive("@media",rules,a)}},directive:function(){var a,c,d,e,h,i;if(b.charAt(g)!=="@")return;if(c=v(this["import"])||v(this.media))return c;if(a=v(/^@page|@keyframes/)||v(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){e=(v(/^[^{]+/)||"").trim();if(d=v(this.block))return new f.Directive(a+" "+e,d)}else if(a=v(/^@[-a-z]+/))if(a==="@font-face"){if(d=v(this.block))return new f.Directive(a,d)}else if((c=v(this.entity))&&v(";"))return new f.Directive(a,c)},font:function(){var a=[],b=[],c,d,e,g;while(g=v(this.shorthand)||v(this.entity))b.push(g);a.push(new f.Expression(b));if(v(","))while(g=v(this.expression)){a.push(g);if(!v(","))break}return new f.Value(a)},value:function(){var a,b=[],c;while(a=v(this.expression)){b.push(a);if(!v(","))break}if(b.length>0)return new f.Value(b)},important:function(){if(b.charAt(g)==="!")return v(/^! *important/)},sub:function(){var a;if(v("(")&&(a=v(this.expression))&&v(")"))return a},multiplication:function(){var a,b,c,d;if(a=v(this.operand)){while(!y(/^\/\*/)&&(c=v("/")||v("*"))&&(b=v(this.operand)))d=new f.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,c,d,e;if(a=v(this.multiplication)){while((d=v(/^[-+]\s+/)||b.charAt(g-1)!=" "&&(v("+")||v("-")))&&(c=v(this.multiplication)))e=new f.Operation(d,[e||a,c]);return e||a}},conditions:function(){var a,b,c=g,d;if(a=v(this.condition)){while(v(",")&&(b=v(this.condition)))d=new f.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,e=g,h=!1;v(/^not/)&&(h=!0),w("(");if(a=v(this.addition)||v(this.entities.keyword)||v(this.entities.quoted))return(d=v(/^(?:>=|=<|[<=>])/))?(b=v(this.addition)||v(this.entities.keyword)||v(this.entities.quoted))?c=new f.Condition(d,a,b,e,h):x("expected expression"):c=new f.Condition("=",a,new f.Keyword("true"),e,h),w(")"),v(/^and/)?new f.Condition("and",c,v(this.condition)):c},operand:function(){var a,c=b.charAt(g+1);b.charAt(g)==="-"&&(c==="@"||c==="(")&&(a=v("-"));var d=v(this.sub)||v(this.entities.dimension)||v(this.entities.color)||v(this.entities.variable)||v(this.entities.call);return a?new f.Operation("*",[new f.Dimension(-1),d]):d},expression:function(){var a,b,c=[],d;while(a=v(this.addition)||v(this.entity))c.push(a);if(c.length>0)return new f.Expression(c)},property:function(){var a;if(a=v(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else if(c.compare)e=c.compare(b);else throw{type:"Type",message:"Unable to perform comparison",index:d};switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d){var e=this;this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(a,b){e.root=b})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;fe.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b){this.name=a,this.index=b},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{message:"variable "+e+" is undefined",index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c){a&&r(a.toCSS(),b,c.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - contents: {}, // Holds the imported file contents - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, contents) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - that.contents[path] = contents; - - if (e && !that.error) { that.error = e } - callback(e, root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input) + 1); - this.callExtract = lines[getLocation(e.call, input)]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - expect(')'); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (! e) { - $('(') && (v = $(this.entities.variable)) && $(')') && (e = new(tree.Paren)(v)); - } - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, index); - } - } - }, - - mediaFeature: function () { - var nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var f, features = []; - while (f = $(this.mediaFeature)) { - features.push(f); - if (! $(',')) { break } - } - return features.length > 0 ? features : null; - }, - - media: function () { - var features; - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - return new(tree.Directive)('@media', rules, features); - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types, e, nodes; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - return this._math('round', n); - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value, features) { - this.name = name; - this.features = features && new(tree.Value)(features); - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.ruleset) { - this.ruleset.root = true; - return this.name + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - this.features = this.features && this.features.eval(env); - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value); -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, index) { - var that = this; - - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root) { - if (e) { e.index = index } - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Directive)('@media', ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, filename: this.filename, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index, filename: this.filename }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []); - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0)); - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors(paths, context, this.selectors); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - var filename = href.match(/([^\/]+)$/)[1]; - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type, - filename: filename - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.2.1.min.js b/dist/less-1.2.1.min.js deleted file mode 100644 index 89b76377c..000000000 --- a/dist/less-1.2.1.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.2.1 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function c(b){return a.less[b.split("/")[1]]}function m(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(v("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function s(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=t(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0||f.status>=200&&f.status<300?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function t(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return v("browser doesn't support AJAX."),null}}function u(a){return a&&a.parentNode.removeChild(a)}function v(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function w(a,b){var c="less-error-message:"+q(b),e='
  • {content}
  • ',f=document.createElement("div"),g,h,i=[],j=a.filename||b;f.id=c,f.className="less-error-message",h="

    "+(a.message||"There is an error in your .less file")+"

    "+'

    in '+j+" ";var k=function(a,b,c){a.extract[b]&&i.push(e.replace(/\{line\}/,parseInt(a.line)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.stack?h+="
    "+a.stack.split("\n").slice(1).join("
    "):a.extract&&(k(a,0,""),k(a,1,"line"),k(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":

    "+"
      "+i.join("")+"
    "),f.innerHTML=h,r([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;cn&&(m[i]=m[i].slice(h-n),n=h)}function w(a){var b,c,d,e,f,j,k,l;if(a instanceof Function)return a.call(o.parsers);if(typeof a=="string")b=g.charAt(h)===a?a:null,d=1,v();else{v();if(!(b=a.exec(m[i])))return null;d=b[0].length}if(b){l=h+=d,j=h+m[i].length-d;while(h=0&&b.charAt(c)!=="\n";c--)d++;return{line:typeof a=="number"?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function C(a,b){var c=A(a,b),d=B(a.index,c),e=d.line,f=d.column,g=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.filename,this.index=a.index,this.line=typeof e=="number"?e+1:null,this.callLine=a.call&&B(a.call,c)+1,this.callExtract=g[B(a.call,c)],this.stack=a.stack,this.column=f,this.extract=[g[e-1],g[e],g[e+1]]}var g,h,i,j,k,l,m,n,o,q=this,r=function(){},s=this.imports={paths:b&&b.paths||[],queue:[],files:{},contents:{},mime:b&&b.mime,error:null,push:function(a,c){var e=this;this.queue.push(a),d.Parser.importer(a,this.paths,function(b,d,f){e.queue.splice(e.queue.indexOf(a),1),e.files[a]=d,e.contents[a]=f,b&&!e.error&&(e.error=b),c(b,d),e.queue.length===0&&r()},b)}};return this.env=b=b||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,o={imports:s,parse:function(a,e){var j,k,p,q,s,t,u=[],v,x=null;h=i=n=l=0,m=[],g=a.replace(/\r\n/g,"\n"),m=function(a){var c=0,d=/[^"'`\{\}\/\(\)]+/g,e=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,f=0,h,i=a[0],j,k;for(var l=0,m,n;l0)throw{type:"Syntax",message:"Missing closing `}`",filename:b.filename};return a.map(function(a){return a.join("")})}([[]]);try{j=new f.Ruleset([],w(this.parsers.primary)),j.root=!0}catch(y){return e(new C(y,b))}j.toCSS=function(a){var e,g,h;return function(e,g){var h=[],i;e=e||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof f.Value||(b instanceof f.Expression||(b=new f.Expression([b])),b=new f.Value([b])),new f.Rule("@"+a,b,!1,0)}),h=[new f.Ruleset(null,g)]);try{var j=a.call(this,{frames:h}).toCSS([],{compress:e.compress||!1})}catch(k){throw new C(k,b)}if(i=o.imports.error)throw i instanceof C?i:new C(i,b);return e.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(j):e.compress?j.replace(/(\s)+/g,"$1"):j}}(j.eval);if(h=0&&g.charAt(z)!=="\n";z--)A++;x={type:"Parse",message:"Syntax Error on line "+s,index:h,filename:b.filename,line:s,column:A,extract:[t[s-2],t[s-1],t[s]]}}this.imports.queue.length>0?r=function(){e(x,j)}:e(x,j)},parsers:{primary:function(){var a,b=[];while((a=w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(g.charAt(h)!=="/")return;if(g.charAt(h+1)==="/")return new f.Comment(w(/^\/\/.*/),!0);if(a=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new f.Comment(a)},entities:{quoted:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=='"'&&g.charAt(b)!=="'")return;c&&w("~");if(a=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new f.Quoted(a[0],a[1]||a[2],c)},keyword:function(){var a;if(a=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return f.colors.hasOwnProperty(a)?new f.Color(f.colors[a].slice(1)):new f.Keyword(a)},call:function(){var a,c,d=h;if(!(a=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(m[i])))return;a=a[1].toLowerCase();if(a==="url")return null;h+=a.length;if(a==="alpha")return w(this.alpha);w("("),c=w(this.entities.arguments);if(!w(")"))return;if(a)return new f.Call(a,c,d,b.filename)},arguments:function(){var a=[],b;while(b=w(this.entities.assignment)||w(this.expression)){a.push(b);if(!w(","))break}return a},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)},assignment:function(){var a,b;if((a=w(/^\w+(?=\s?=)/i))&&w("=")&&(b=w(this.entity)))return new f.Assignment(a,b)},url:function(){var a;if(g.charAt(h)!=="u"||!w(/^url\(/))return;return a=w(this.entities.quoted)||w(this.entities.variable)||w(this.entities.dataURI)||w(/^[-\w%@$\/.&=:;#+?~]+/)||"",x(")"),new f.URL(a.value||a.data||a instanceof f.Variable?a:new f.Anonymous(a),s.paths)},dataURI:function(){var a;if(w(/^data:/)){a={},a.mime=w(/^[^\/]+\/[^,;)]+/)||"",a.charset=w(/^;\s*charset=[^,;)]+/)||"",a.base64=w(/^;\s*base64/)||"",a.data=w(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,c=h;if(g.charAt(h)==="@"&&(a=w(/^@@?[\w-]+/)))return new f.Variable(a,c,b.filename)},color:function(){var a;if(g.charAt(h)==="#"&&(a=w(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new f.Color(a[1])},dimension:function(){var a,b=g.charCodeAt(h);if(b>57||b<45||b===47)return;if(a=w(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new f.Dimension(a[1],a[2])},javascript:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=="`")return;c&&w("~");if(a=w(/^`([^`]*)`/))return new f.JavaScript(a[1],h,c)}},variable:function(){var a;if(g.charAt(h)==="@"&&(a=w(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!z(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=w(this.entity))&&w("/")&&(b=w(this.entity)))return new f.Shorthand(a,b)},mixin:{call:function(){var a=[],c,d,e,i=h,j=g.charAt(h),k=!1;if(j!=="."&&j!=="#")return;while(c=w(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new f.Element(d,c,h)),d=w(">");w("(")&&(e=w(this.entities.arguments))&&w(")"),w(this.important)&&(k=!0);if(a.length>0&&(w(";")||z("}")))return new f.mixin.Call(a,e,i,b.filename,k)},definition:function(){var a,b=[],c,d,e,i,j;if(g.charAt(h)!=="."&&g.charAt(h)!=="#"||z(/^[^{]*(;|})/))return;t();if(c=w(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=c[1];while(e=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)){e instanceof f.Variable?w(":")?(i=x(this.expression,"expected expression"),b.push({name:e.name,value:i})):b.push({name:e.name}):b.push({value:e});if(!w(","))break}x(")"),w(/^when/)&&(j=x(this.conditions,"expected condition")),d=w(this.block);if(d)return new f.mixin.Definition(a,b,d,j);u()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||z("}")},alpha:function(){var a;if(!w(/^\(opacity=/i))return;if(a=w(/^\d+/)||w(this.entities.variable))return x(")"),new f.Alpha(a)},element:function(){var a,b,c,d;c=w(this.combinator),a=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||w("*")||w(this.attribute)||w(/^\([^)@]+\)/),a||w("(")&&(d=w(this.entities.variable))&&w(")")&&(a=new f.Paren(d));if(a)return new f.Element(c,a,h);if(c.value&&c.value.charAt(0)==="&")return new f.Element(c,null,h)},combinator:function(){var a,b=g.charAt(h);if(b===">"||b==="+"||b==="~"){h++;while(g.charAt(h)===" ")h++;return new f.Combinator(b)}if(b==="&"){a="&",h++,g.charAt(h)===" "&&(a="& ");while(g.charAt(h)===" ")h++;return new f.Combinator(a)}if(b===":"&&g.charAt(h+1)===":"){h+=2;while(g.charAt(h)===" ")h++;return new f.Combinator("::")}return g.charAt(h-1)===" "?new f.Combinator(" "):new f.Combinator(null)},selector:function(){var a,b,c=[],d,e;while(b=w(this.element)){d=g.charAt(h),c.push(b);if(d==="{"||d==="}"||d===";"||d===",")break}if(c.length>0)return new f.Selector(c)},tag:function(){return w(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||w("*")},attribute:function(){var a="",b,c,d;if(!w("["))return;if(b=w(/^[a-zA-Z-]+/)||w(this.entities.quoted))(d=w(/^[|~*$^]?=/))&&(c=w(this.entities.quoted)||w(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!w("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(w("{")&&(a=w(this.primary))&&w("}"))return a},ruleset:function(){var a=[],b,c,d;t();while(b=w(this.selector)){a.push(b),w(this.comment);if(!w(","))break;w(this.comment)}if(a.length>0&&(c=w(this.block)))return new f.Ruleset(a,c);l=h,u()},rule:function(){var a,b,c=g.charAt(h),d,e;t();if(c==="."||c==="#"||c==="&")return;if(a=w(this.variable)||w(this.property)){a.charAt(0)!="@"&&(e=/^([^@+\/'"*`(;{}-]*);/.exec(m[i]))?(h+=e[0].length-1,b=new f.Anonymous(e[1])):a==="font"?b=w(this.font):b=w(this.value),d=w(this.important);if(b&&w(this.end))return new f.Rule(a,b,d,k);l=h,u()}},"import":function(){var a,b,c=h;if(w(/^@import\s+/)&&(a=w(this.entities.quoted)||w(this.entities.url))){b=w(this.mediaFeatures);if(w(";"))return new f.Import(a,s,b,c)}},mediaFeature:function(){var a=[];do if(e=w(this.entities.keyword))a.push(e);else if(w("(")){p=w(this.property),e=w(this.entity);if(!w(")"))return null;if(p&&e)a.push(new f.Paren(new f.Rule(p,e,null,h,!0)));else{if(!e)return null;a.push(new f.Paren(e))}}while(e);if(a.length>0)return new f.Expression(a)},mediaFeatures:function(){var a,b=[];while(a=w(this.mediaFeature)){b.push(a);if(!w(","))break}return b.length>0?b:null},media:function(){var a;if(w(/^@media/)){a=w(this.mediaFeatures);if(rules=w(this.block))return new f.Directive("@media",rules,a)}},directive:function(){var a,b,c,d,e,i;if(g.charAt(h)!=="@")return;if(b=w(this["import"])||w(this.media))return b;if(a=w(/^@page|@keyframes/)||w(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){d=(w(/^[^{]+/)||"").trim();if(c=w(this.block))return new f.Directive(a+" "+d,c)}else if(a=w(/^@[-a-z]+/))if(a==="@font-face"){if(c=w(this.block))return new f.Directive(a,c)}else if((b=w(this.entity))&&w(";"))return new f.Directive(a,b)},font:function(){var a=[],b=[],c,d,e,g;while(g=w(this.shorthand)||w(this.entity))b.push(g);a.push(new f.Expression(b));if(w(","))while(g=w(this.expression)){a.push(g);if(!w(","))break}return new f.Value(a)},value:function(){var a,b=[],c;while(a=w(this.expression)){b.push(a);if(!w(","))break}if(b.length>0)return new f.Value(b)},important:function(){if(g.charAt(h)==="!")return w(/^! *important/)},sub:function(){var a;if(w("(")&&(a=w(this.expression))&&w(")"))return a},multiplication:function(){var a,b,c,d;if(a=w(this.operand)){while(!z(/^\/\*/)&&(c=w("/")||w("*"))&&(b=w(this.operand)))d=new f.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,b,c,d;if(a=w(this.multiplication)){while((c=w(/^[-+]\s+/)||g.charAt(h-1)!=" "&&(w("+")||w("-")))&&(b=w(this.multiplication)))d=new f.Operation(c,[d||a,b]);return d||a}},conditions:function(){var a,b,c=h,d;if(a=w(this.condition)){while(w(",")&&(b=w(this.condition)))d=new f.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,e=h,g=!1;w(/^not/)&&(g=!0),x("(");if(a=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(d=w(/^(?:>=|=<|[<=>])/))?(b=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?c=new f.Condition(d,a,b,e,g):y("expected expression"):c=new f.Condition("=",a,new f.Keyword("true"),e,g),x(")"),w(/^and/)?new f.Condition("and",c,w(this.condition)):c},operand:function(){var a,b=g.charAt(h+1);g.charAt(h)==="-"&&(b==="@"||b==="(")&&(a=w("-"));var c=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return a?new f.Operation("*",[new f.Dimension(-1),c]):c},expression:function(){var a,b,c=[],d;while(a=w(this.addition)||w(this.entity))c.push(a);if(c.length>0)return new f.Expression(c)},property:function(){var a;if(a=w(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else{if(!c.compare)throw{type:"Type",message:"Unable to perform comparison",index:d};e=c.compare(b)}switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d,e){var f=this;this.index=e,this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(b,c){b&&(b.index=e),f.root=c||new a.Ruleset([],[])})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;fe.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.file=c},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{type:"Name",message:"variable "+e+" is undefined",filename:this.file,index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c,d,e){b&&r(b.toCSS(),d,e.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - contents: {}, // Holds the imported file contents - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, contents) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - that.contents[path] = contents; - - if (e && !that.error) { that.error = e } - callback(e, root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function basename(pathname) { - if (less.mode === 'node') { - return require('path').basename(pathname); - } else { - return pathname.match(/[^\/]+$/)[0]; - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[basename(e.filename)]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input) + 1); - this.callExtract = lines[getLocation(e.call, input)]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)\\]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - } - if (level > 0) { - error = new(LessError)({ - index: i, - type: 'Parse', - message: "missing closing `}`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - expect(')'); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (! e) { - $('(') && (v = $(this.entities.variable)) && $(')') && (e = new(tree.Paren)(v)); - } - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, index); - } - } - }, - - mediaFeature: function () { - var nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var f, features = []; - while (f = $(this.mediaFeature)) { - features.push(f); - if (! $(',')) { break } - } - return features.length > 0 ? features : null; - }, - - media: function () { - var features; - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - return new(tree.Directive)('@media', rules, features); - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types, e, nodes; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, function (e) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.apply(null, arguments); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - return this._math('round', n); - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value, features) { - this.name = name; - this.features = features && new(tree.Value)(features); - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.ruleset) { - this.ruleset.root = true; - return this.name + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - this.features = this.features && this.features.eval(env); - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value); -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, index) { - var that = this; - - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root) { - if (e) { e.index = index } - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Directive)('@media', ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, filename: this.filename, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index, filename: this.filename }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []); - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0)); - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors(paths, context, this.selectors); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join( env.compress ? ',' : ',\n'); - - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - var filename = href.match(/([^\/]+)$/)[1]; - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type, - filename: filename - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.2.2.min.js b/dist/less-1.2.2.min.js deleted file mode 100644 index 55042ecb8..000000000 --- a/dist/less-1.2.2.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.2.2 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function c(b){return a.less[b.split("/")[1]]}function m(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(v("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function s(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=t(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0||f.status>=200&&f.status<300?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function t(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return v("browser doesn't support AJAX."),null}}function u(a){return a&&a.parentNode.removeChild(a)}function v(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function w(a,b){var c="less-error-message:"+q(b),e='
  • {content}
  • ',f=document.createElement("div"),g,h,i=[],j=a.filename||b;f.id=c,f.className="less-error-message",h="

    "+(a.message||"There is an error in your .less file")+"

    "+'

    in '+j+" ";var k=function(a,b,c){a.extract[b]&&i.push(e.replace(/\{line\}/,parseInt(a.line)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.stack?h+="
    "+a.stack.split("\n").slice(1).join("
    "):a.extract&&(k(a,0,""),k(a,1,"line"),k(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":

    "+"
      "+i.join("")+"
    "),f.innerHTML=h,r([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}typeof define=="function"&&define.amd&&define("less",[],function(){return d}),Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;cn&&(m[i]=m[i].slice(h-n),n=h)}function w(a){var b,c,d,e,f,j,k,l;if(a instanceof Function)return a.call(o.parsers);if(typeof a=="string")b=g.charAt(h)===a?a:null,d=1,v();else{v();if(!(b=a.exec(m[i])))return null;d=b[0].length}if(b){l=h+=d,j=h+m[i].length-d;while(h=0&&b.charAt(c)!=="\n";c--)d++;return{line:typeof a=="number"?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function D(a,b){var c=B(a,b),d=C(a.index,c),e=d.line,f=d.column,g=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.filename,this.index=a.index,this.line=typeof e=="number"?e+1:null,this.callLine=a.call&&C(a.call,c)+1,this.callExtract=g[C(a.call,c)],this.stack=a.stack,this.column=f,this.extract=[g[e-1],g[e],g[e+1]]}var g,h,i,j,k,l,m,n,o,q=this,r=function(){},s=this.imports={paths:b&&b.paths||[],queue:[],files:{},contents:{},mime:b&&b.mime,error:null,push:function(a,c){var e=this;this.queue.push(a),d.Parser.importer(a,this.paths,function(b,d,f){e.queue.splice(e.queue.indexOf(a),1),e.files[a]=d,e.contents[a]=f,b&&!e.error&&(e.error=b),c(b,d),e.queue.length===0&&r()},b)}};return this.env=b=b||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,o={imports:s,parse:function(a,e){var j,k,p,q,s,t,u=[],v,x=null;h=i=n=l=0,g=a.replace(/\r\n/g,"\n"),m=function(a){var c=0,d=/[^"'`\{\}\/\(\)\\]+/g,e=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,f=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g,h=0,i,j=a[0],k;for(var l=0,m,n;l0&&(x=new D({index:l,type:"Parse",message:"missing closing `}`",filename:b.filename},b)),a.map(function(a){return a.join("")})}([[]]);if(x)return e(x);try{j=new f.Ruleset([],w(this.parsers.primary)),j.root=!0}catch(y){return e(new D(y,b))}j.toCSS=function(a){var e,g,h;return function(e,g){var h=[],i;e=e||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof f.Value||(b instanceof f.Expression||(b=new f.Expression([b])),b=new f.Value([b])),new f.Rule("@"+a,b,!1,0)}),h=[new f.Ruleset(null,g)]);try{var j=a.call(this,{frames:h}).toCSS([],{compress:e.compress||!1})}catch(k){throw new D(k,b)}if(i=o.imports.error)throw i instanceof D?i:new D(i,b);return e.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(j):e.compress?j.replace(/(\s)+/g,"$1"):j}}(j.eval);if(h=0&&g.charAt(z)!=="\n";z--)A++;x={type:"Parse",message:"Syntax Error on line "+s,index:h,filename:b.filename,line:s,column:A,extract:[t[s-2],t[s-1],t[s]]}}this.imports.queue.length>0?r=function(){e(x,j)}:e(x,j)},parsers:{primary:function(){var a,b=[];while((a=w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(g.charAt(h)!=="/")return;if(g.charAt(h+1)==="/")return new f.Comment(w(/^\/\/.*/),!0);if(a=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new f.Comment(a)},entities:{quoted:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=='"'&&g.charAt(b)!=="'")return;c&&w("~");if(a=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new f.Quoted(a[0],a[1]||a[2],c)},keyword:function(){var a;if(a=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return f.colors.hasOwnProperty(a)?new f.Color(f.colors[a].slice(1)):new f.Keyword(a)},call:function(){var a,c,d=h;if(!(a=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(m[i])))return;a=a[1].toLowerCase();if(a==="url")return null;h+=a.length;if(a==="alpha")return w(this.alpha);w("("),c=w(this.entities.arguments);if(!w(")"))return;if(a)return new f.Call(a,c,d,b.filename)},arguments:function(){var a=[],b;while(b=w(this.entities.assignment)||w(this.expression)){a.push(b);if(!w(","))break}return a},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)},assignment:function(){var a,b;if((a=w(/^\w+(?=\s?=)/i))&&w("=")&&(b=w(this.entity)))return new f.Assignment(a,b)},url:function(){var a;if(g.charAt(h)!=="u"||!w(/^url\(/))return;return a=w(this.entities.quoted)||w(this.entities.variable)||w(this.entities.dataURI)||w(/^[-\w%@$\/.&=:;#+?~]+/)||"",x(")"),new f.URL(a.value||a.data||a instanceof f.Variable?a:new f.Anonymous(a),s.paths)},dataURI:function(){var a;if(w(/^data:/)){a={},a.mime=w(/^[^\/]+\/[^,;)]+/)||"",a.charset=w(/^;\s*charset=[^,;)]+/)||"",a.base64=w(/^;\s*base64/)||"",a.data=w(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,c=h;if(g.charAt(h)==="@"&&(a=w(/^@@?[\w-]+/)))return new f.Variable(a,c,b.filename)},color:function(){var a;if(g.charAt(h)==="#"&&(a=w(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new f.Color(a[1])},dimension:function(){var a,b=g.charCodeAt(h);if(b>57||b<45||b===47)return;if(a=w(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new f.Dimension(a[1],a[2])},javascript:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=="`")return;c&&w("~");if(a=w(/^`([^`]*)`/))return new f.JavaScript(a[1],h,c)}},variable:function(){var a;if(g.charAt(h)==="@"&&(a=w(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!z(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=w(this.entity))&&w("/")&&(b=w(this.entity)))return new f.Shorthand(a,b)},mixin:{call:function(){var a=[],c,d,e,i=h,j=g.charAt(h),k=!1;if(j!=="."&&j!=="#")return;while(c=w(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new f.Element(d,c,h)),d=w(">");w("(")&&(e=w(this.entities.arguments))&&w(")"),w(this.important)&&(k=!0);if(a.length>0&&(w(";")||z("}")))return new f.mixin.Call(a,e,i,b.filename,k)},definition:function(){var a,b=[],c,d,e,i,j;if(g.charAt(h)!=="."&&g.charAt(h)!=="#"||z(/^[^{]*(;|})/))return;t();if(c=w(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=c[1];while(e=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)){e instanceof f.Variable?w(":")?(i=x(this.expression,"expected expression"),b.push({name:e.name,value:i})):b.push({name:e.name}):b.push({value:e});if(!w(","))break}x(")"),w(/^when/)&&(j=x(this.conditions,"expected condition")),d=w(this.block);if(d)return new f.mixin.Definition(a,b,d,j);u()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||z("}")},alpha:function(){var a;if(!w(/^\(opacity=/i))return;if(a=w(/^\d+/)||w(this.entities.variable))return x(")"),new f.Alpha(a)},element:function(){var a,b,c,d;c=w(this.combinator),a=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||w("*")||w(this.attribute)||w(/^\([^)@]+\)/),a||w("(")&&(d=w(this.entities.variable))&&w(")")&&(a=new f.Paren(d));if(a)return new f.Element(c,a,h);if(c.value&&c.value.charAt(0)==="&")return new f.Element(c,null,h)},combinator:function(){var a,b=g.charAt(h);if(b===">"||b==="+"||b==="~"){h++;while(g.charAt(h)===" ")h++;return new f.Combinator(b)}if(b==="&"){a="&",h++,g.charAt(h)===" "&&(a="& ");while(g.charAt(h)===" ")h++;return new f.Combinator(a)}if(b===":"&&g.charAt(h+1)===":"){h+=2;while(g.charAt(h)===" ")h++;return new f.Combinator("::")}return g.charAt(h-1)===" "?new f.Combinator(" "):new f.Combinator(null)},selector:function(){var a,b,c=[],d,e;while(b=w(this.element)){d=g.charAt(h),c.push(b);if(d==="{"||d==="}"||d===";"||d===",")break}if(c.length>0)return new f.Selector(c)},tag:function(){return w(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||w("*")},attribute:function(){var a="",b,c,d;if(!w("["))return;if(b=w(/^[a-zA-Z-]+/)||w(this.entities.quoted))(d=w(/^[|~*$^]?=/))&&(c=w(this.entities.quoted)||w(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!w("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(w("{")&&(a=w(this.primary))&&w("}"))return a},ruleset:function(){var a=[],b,c,d;t();while(b=w(this.selector)){a.push(b),w(this.comment);if(!w(","))break;w(this.comment)}if(a.length>0&&(c=w(this.block)))return new f.Ruleset(a,c);l=h,u()},rule:function(){var a,b,c=g.charAt(h),d,e;t();if(c==="."||c==="#"||c==="&")return;if(a=w(this.variable)||w(this.property)){a.charAt(0)!="@"&&(e=/^([^@+\/'"*`(;{}-]*);/.exec(m[i]))?(h+=e[0].length-1,b=new f.Anonymous(e[1])):a==="font"?b=w(this.font):b=w(this.value),d=w(this.important);if(b&&w(this.end))return new f.Rule(a,b,d,k);l=h,u()}},"import":function(){var a,b,c=h;if(w(/^@import\s+/)&&(a=w(this.entities.quoted)||w(this.entities.url))){b=w(this.mediaFeatures);if(w(";"))return new f.Import(a,s,b,c)}},mediaFeature:function(){var a=[];do if(e=w(this.entities.keyword))a.push(e);else if(w("(")){p=w(this.property),e=w(this.entity);if(!w(")"))return null;if(p&&e)a.push(new f.Paren(new f.Rule(p,e,null,h,!0)));else{if(!e)return null;a.push(new f.Paren(e))}}while(e);if(a.length>0)return new f.Expression(a)},mediaFeatures:function(){var a,b=[];while(a=w(this.mediaFeature)){b.push(a);if(!w(","))break}return b.length>0?b:null},media:function(){var a;if(w(/^@media/)){a=w(this.mediaFeatures);if(rules=w(this.block))return new f.Directive("@media",rules,a)}},directive:function(){var a,b,c,d,e,i;if(g.charAt(h)!=="@")return;if(b=w(this["import"])||w(this.media))return b;if(a=w(/^@page|@keyframes/)||w(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){d=(w(/^[^{]+/)||"").trim();if(c=w(this.block))return new f.Directive(a+" "+d,c)}else if(a=w(/^@[-a-z]+/))if(a==="@font-face"){if(c=w(this.block))return new f.Directive(a,c)}else if((b=w(this.entity))&&w(";"))return new f.Directive(a,b)},font:function(){var a=[],b=[],c,d,e,g;while(g=w(this.shorthand)||w(this.entity))b.push(g);a.push(new f.Expression(b));if(w(","))while(g=w(this.expression)){a.push(g);if(!w(","))break}return new f.Value(a)},value:function(){var a,b=[],c;while(a=w(this.expression)){b.push(a);if(!w(","))break}if(b.length>0)return new f.Value(b)},important:function(){if(g.charAt(h)==="!")return w(/^! *important/)},sub:function(){var a;if(w("(")&&(a=w(this.expression))&&w(")"))return a},multiplication:function(){var a,b,c,d;if(a=w(this.operand)){while(!z(/^\/\*/)&&(c=w("/")||w("*"))&&(b=w(this.operand)))d=new f.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,b,c,d;if(a=w(this.multiplication)){while((c=w(/^[-+]\s+/)||g.charAt(h-1)!=" "&&(w("+")||w("-")))&&(b=w(this.multiplication)))d=new f.Operation(c,[d||a,b]);return d||a}},conditions:function(){var a,b,c=h,d;if(a=w(this.condition)){while(w(",")&&(b=w(this.condition)))d=new f.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,e=h,g=!1;w(/^not/)&&(g=!0),x("(");if(a=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(d=w(/^(?:>=|=<|[<=>])/))?(b=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?c=new f.Condition(d,a,b,e,g):y("expected expression"):c=new f.Condition("=",a,new f.Keyword("true"),e,g),x(")"),w(/^and/)?new f.Condition("and",c,w(this.condition)):c},operand:function(){var a,b=g.charAt(h+1);g.charAt(h)==="-"&&(b==="@"||b==="(")&&(a=w("-"));var c=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return a?new f.Operation("*",[new f.Dimension(-1),c]):c},expression:function(){var a,b,c=[],d;while(a=w(this.addition)||w(this.entity))c.push(a);if(c.length>0)return new f.Expression(c)},property:function(){var a;if(a=w(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){!/^([a-z]+:)?\//.test(a)&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},function(e){e&&typeof d.errback=="function"?d.errback.call(null,a,b,c,d):c.apply(null,arguments)},!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else{if(!c.compare)throw{type:"Type",message:"Unable to perform comparison",index:d};e=c.compare(b)}switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d,e){var f=this;this.index=e,this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(b,c){b&&(b.index=e),f.root=c||new a.Ruleset([],[])})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g0&&c>this.params.length)return!1;if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;fe.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":",\n"),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.file=c},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{type:"Name",message:"variable "+e+" is undefined",filename:this.file,index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c,d,e){b&&r(b.toCSS(),d,e.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - contents: {}, // Holds the imported file contents - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, contents) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - that.contents[path] = contents; - - if (e && !that.error) { that.error = e } - callback(e, root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function basename(pathname) { - if (less.mode === 'node') { - return require('path').basename(pathname); - } else { - return pathname.match(/[^\/]+$/)[0]; - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[basename(e.filename)]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)\\]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - } - if (level > 0) { - error = new(LessError)({ - index: i, - type: 'Parse', - message: "missing closing `}`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args || [], index, env.filename, important); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - do { - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',')) - - expect(')'); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (! e) { - $('(') && (v = $(this.entities.variable)) && $(')') && (e = new(tree.Paren)(v)); - } - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - if ($('(')) { - sel = $(this.entity); - expect(')'); - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules, env.strictImports); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, index); - } - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules; - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - return new(tree.Media)(rules, features); - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types, e, nodes; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } else if (name = $(/^@page|@keyframes/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, function (e) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.apply(null, arguments); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - return this._math('round', n); - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value, features) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value); -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, index) { - var that = this; - - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root) { - if (e) { e.index = index } - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var el = new(tree.Element)('&', null, 0), - selectors = [new(tree.Selector)([el])]; - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var blockIndex = env.mediaBlocks.length; - env.mediaPath.push(this); - env.mediaBlocks.push(this); - - var media = new(tree.Media)([], []); - media.features = this.features.eval(env); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaBlocks[blockIndex] = media; - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var el = new(tree.Element)('&', null, 0); - var selectors = [new(tree.Selector)([el])]; - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index, filename: this.filename }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []), varargs; - - for (var i = 0, val, name; i < this.params.length; i++) { - if (name = this.params[i].name) { - if (this.params[i].variadic && args) { - varargs = []; - for (var j = i; j < args.length; j++) { - varargs.push(args[j].eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules, start; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors(paths, context, this.selectors); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive) || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join( env.compress ? ',' : ',\n'); - - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = (location.protocol === 'file:' || - location.protocol === 'chrome:' || - location.protocol === 'chrome-extension:' || - location.protocol === 'resource:'); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^(https?|file):/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - var filename = href.match(/([^\/]+)$/)[1]; - - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type, - filename: filename - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - css.media = sheet.media || 'screen'; - css.id = id; - document.getElementsByTagName('head')[0].appendChild(css); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? false : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -})(window); diff --git a/dist/less-1.3.0.min.js b/dist/less-1.3.0.min.js deleted file mode 100644 index 309bf550d..000000000 --- a/dist/less-1.3.0.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.3.0 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(a,b){function c(b){return a.less[b.split("/")[1]]}function l(){var a=document.getElementsByTagName("style");for(var b=0;b0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&g&&(t("saving "+e+" to cache."),g.setItem(e,a),g.setItem(e+":timestamp",c))}function q(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var g=r(),h=f?!1:d.async;typeof g.overrideMimeType=="function"&&g.overrideMimeType("text/css"),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),f?g.status===0||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){g.readyState==4&&i(g,c,e)}:i(g,c,e)}function r(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return t("browser doesn't support AJAX."),null}}function s(a){return a&&a.parentNode.removeChild(a)}function t(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function u(a,b){var c="less-error-message:"+o(b),e='
  • {content}
  • ',f=document.createElement("div"),g,h,i=[],j=a.filename||b;f.id=c,f.className="less-error-message",h="

    "+(a.message||"There is an error in your .less file")+"

    "+'

    in '+j+" ";var k=function(a,b,c){a.extract[b]&&i.push(e.replace(/\{line\}/,parseInt(a.line)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.stack?h+="
    "+a.stack.split("\n").slice(1).join("
    "):a.extract&&(k(a,0,""),k(a,1,"line"),k(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":

    "+"
      "+i.join("")+"
    "),f.innerHTML=h,p([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}typeof define=="function"&&define.amd&&define("less",[],function(){return d}),Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d>>0,c=new Array(b),d=arguments[1];for(var e=0;e>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c=b)return-1;c<0&&(c+=b);for(;cl&&(k[g]=k[g].slice(f-l),l=f)}function t(a){var c,d,e,h,i,j,n,o;if(a instanceof Function)return a.call(m.parsers);if(typeof a=="string")c=b.charAt(f)===a?a:null,e=1,s();else{s();if(c=a.exec(k[g]))e=c[0].length;else return null}if(c){o=f+=e,j=f+k[g].length-e;while(f=0&&b.charAt(c)!=="\n";c--)d++;return{line:typeof a=="number"?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function A(a,b){var c=y(a,b),d=z(a.index,c),e=d.line,f=d.column,g=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.filename,this.index=a.index,this.line=typeof e=="number"?e+1:null,this.callLine=a.call&&z(a.call,c).line+1,this.callExtract=g[z(a.call,c).line],this.stack=a.stack,this.column=f,this.extract=[g[e-1],g[e],g[e+1]]}var b,f,g,h,i,j,k,l,m,n=this,o=function(){},p=this.imports={paths:a&&a.paths||[],queue:[],files:{},contents:{},mime:a&&a.mime,error:null,push:function(b,c){var e=this;this.queue.push(b),d.Parser.importer(b,this.paths,function(a,d,f){e.queue.splice(e.queue.indexOf(b),1),e.files[b]=d,e.contents[b]=f,a&&!e.error&&(e.error=a),c(a,d),e.queue.length===0&&o()},a)}};return this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,m={imports:p,parse:function(h,i){var n,p,q,r,s,u,v=[],w,x=null;f=g=l=j=0,b=h.replace(/\r\n/g,"\n"),k=function(c){var d=0,e=/[^"'`\{\}\/\(\)\\]+/g,f=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,g=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`\\\r\n]|\\.)*)`/g,h=0,i,j=c[0],k;for(var l=0,m,n;l0&&(x=new A({index:l,type:"Parse",message:"missing closing `}`",filename:a.filename},a)),c.map(function(a){return a.join("")})}([[]]);if(x)return i(x);try{n=new e.Ruleset([],t(this.parsers.primary)),n.root=!0}catch(y){return i(new A(y,a))}n.toCSS=function(b){var f,g,h;return function(f,g){var h=[],i;f=f||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b])),new e.Rule("@"+a,b,!1,0)}),h=[new e.Ruleset(null,g)]);try{var j=b.call(this,{frames:h}).toCSS([],{compress:f.compress||!1})}catch(k){throw new A(k,a)}if(i=m.imports.error)throw i instanceof A?i:new A(i,a);return f.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(j):f.compress?j.replace(/(\s)+/g,"$1"):j}}(n.eval);if(f=0&&b.charAt(z)!=="\n";z--)B++;x={type:"Parse",message:"Syntax Error on line "+s,index:f,filename:a.filename,line:s,column:B,extract:[u[s-2],u[s-1],u[s]]}}this.imports.queue.length>0?o=function(){i(x,n)}:i(x,n)},parsers:{primary:function(){var a,b=[];while((a=t(this.mixin.definition)||t(this.rule)||t(this.ruleset)||t(this.mixin.call)||t(this.comment)||t(this.directive))||t(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(b.charAt(f)!=="/")return;if(b.charAt(f+1)==="/")return new e.Comment(t(/^\/\/.*/),!0);if(a=t(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new e.Comment(a)},entities:{quoted:function(){var a,c=f,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=='"'&&b.charAt(c)!=="'")return;d&&t("~");if(a=t(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new e.Quoted(a[0],a[1]||a[2],d)},keyword:function(){var a;if(a=t(/^[_A-Za-z-][_A-Za-z0-9-]*/))return e.colors.hasOwnProperty(a)?new e.Color(e.colors[a].slice(1)):new e.Keyword(a)},call:function(){var b,c,d=f;if(!(b=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(k[g])))return;b=b[1].toLowerCase();if(b==="url")return null;f+=b.length;if(b==="alpha")return t(this.alpha);t("("),c=t(this.entities.arguments);if(!t(")"))return;if(b)return new e.Call(b,c,d,a.filename)},arguments:function(){var a=[],b;while(b=t(this.entities.assignment)||t(this.expression)){a.push(b);if(!t(","))break}return a},literal:function(){return t(this.entities.dimension)||t(this.entities.color)||t(this.entities.quoted)},assignment:function(){var a,b;if((a=t(/^\w+(?=\s?=)/i))&&t("=")&&(b=t(this.entity)))return new e.Assignment(a,b)},url:function(){var a;if(b.charAt(f)!=="u"||!t(/^url\(/))return;return a=t(this.entities.quoted)||t(this.entities.variable)||t(this.entities.dataURI)||t(/^[-\w%@$\/.&=:;#+?~]+/)||"",u(")"),new e.URL(a.value||a.data||a instanceof e.Variable?a:new e.Anonymous(a),p.paths)},dataURI:function(){var a;if(t(/^data:/)){a={},a.mime=t(/^[^\/]+\/[^,;)]+/)||"",a.charset=t(/^;\s*charset=[^,;)]+/)||"",a.base64=t(/^;\s*base64/)||"",a.data=t(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var c,d=f;if(b.charAt(f)==="@"&&(c=t(/^@@?[\w-]+/)))return new e.Variable(c,d,a.filename)},color:function(){var a;if(b.charAt(f)==="#"&&(a=t(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new e.Color(a[1])},dimension:function(){var a,c=b.charCodeAt(f);if(c>57||c<45||c===47)return;if(a=t(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new e.Dimension(a[1],a[2])},javascript:function(){var a,c=f,d;b.charAt(c)==="~"&&(c++,d=!0);if(b.charAt(c)!=="`")return;d&&t("~");if(a=t(/^`([^`]*)`/))return new e.JavaScript(a[1],f,d)}},variable:function(){var a;if(b.charAt(f)==="@"&&(a=t(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!w(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=t(this.entity))&&t("/")&&(b=t(this.entity)))return new e.Shorthand(a,b)},mixin:{call:function(){var c=[],d,g,h,i=f,j=b.charAt(f),k=!1;if(j!=="."&&j!=="#")return;while(d=t(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))c.push(new e.Element(g,d,f)),g=t(">");t("(")&&(h=t(this.entities.arguments))&&t(")"),t(this.important)&&(k=!0);if(c.length>0&&(t(";")||w("}")))return new e.mixin.Call(c,h||[],i,a.filename,k)},definition:function(){var a,c=[],d,g,h,i,j,k=!1;if(b.charAt(f)!=="."&&b.charAt(f)!=="#"||w(/^[^{]*(;|})/))return;q();if(d=t(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=d[1];do{if(b.charAt(f)==="."&&t(/^\.{3}/)){k=!0;break}if(!(h=t(this.entities.variable)||t(this.entities.literal)||t(this.entities.keyword)))break;if(h instanceof e.Variable)if(t(":"))i=u(this.expression,"expected expression"),c.push({name:h.name,value:i});else{if(t(/^\.{3}/)){c.push({name:h.name,variadic:!0}),k=!0;break}c.push({name:h.name})}else c.push({value:h})}while(t(","));u(")"),t(/^when/)&&(j=u(this.conditions,"expected condition")),g=t(this.block);if(g)return new e.mixin.Definition(a,c,g,j,k);r()}}},entity:function(){return t(this.entities.literal)||t(this.entities.variable)||t(this.entities.url)||t(this.entities.call)||t(this.entities.keyword)||t(this.entities.javascript)||t(this.comment)},end:function(){return t(";")||w("}")},alpha:function(){var a;if(!t(/^\(opacity=/i))return;if(a=t(/^\d+/)||t(this.entities.variable))return u(")"),new e.Alpha(a)},element:function(){var a,b,c,d;c=t(this.combinator),a=t(/^(?:\d+\.\d+|\d+)%/)||t(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||t("*")||t(this.attribute)||t(/^\([^)@]+\)/),a||t("(")&&(d=t(this.entities.variable))&&t(")")&&(a=new e.Paren(d));if(a)return new e.Element(c,a,f);if(c.value&&c.value.charAt(0)==="&")return new e.Element(c,null,f)},combinator:function(){var a,c=b.charAt(f);if(c===">"||c==="+"||c==="~"){f++;while(b.charAt(f)===" ")f++;return new e.Combinator(c)}if(c==="&"){a="&",f++,b.charAt(f)===" "&&(a="& ");while(b.charAt(f)===" ")f++;return new e.Combinator(a)}return b.charAt(f-1)===" "?new e.Combinator(" "):new e.Combinator(null)},selector:function(){var a,c,d=[],g,h;if(t("("))return a=t(this.entity),u(")"),new e.Selector([new e.Element("",a,f)]);while(c=t(this.element)){g=b.charAt(f),d.push(c);if(g==="{"||g==="}"||g===";"||g===",")break}if(d.length>0)return new e.Selector(d)},tag:function(){return t(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||t("*")},attribute:function(){var a="",b,c,d;if(!t("["))return;if(b=t(/^[a-zA-Z-]+/)||t(this.entities.quoted))(d=t(/^[|~*$^]?=/))&&(c=t(this.entities.quoted)||t(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!t("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(t("{")&&(a=t(this.primary))&&t("}"))return a},ruleset:function(){var b=[],c,d,g;q();while(c=t(this.selector)){b.push(c),t(this.comment);if(!t(","))break;t(this.comment)}if(b.length>0&&(d=t(this.block)))return new e.Ruleset(b,d,a.strictImports);j=f,r()},rule:function(){var a,c,d=b.charAt(f),h,l;q();if(d==="."||d==="#"||d==="&")return;if(a=t(this.variable)||t(this.property)){a.charAt(0)!="@"&&(l=/^([^@+\/'"*`(;{}-]*);/.exec(k[g]))?(f+=l[0].length-1,c=new e.Anonymous(l[1])):a==="font"?c=t(this.font):c=t(this.value),h=t(this.important);if(c&&t(this.end))return new e.Rule(a,c,h,i);j=f,r()}},"import":function(){var a,b,c=f;if(t(/^@import\s+/)&&(a=t(this.entities.quoted)||t(this.entities.url))){b=t(this.mediaFeatures);if(t(";"))return new e.Import(a,p,b,c)}},mediaFeature:function(){var a,b,c=[];do if(a=t(this.entities.keyword))c.push(a);else if(t("(")){b=t(this.property),a=t(this.entity);if(!t(")"))return null;if(b&&a)c.push(new e.Paren(new e.Rule(b,a,null,f,!0)));else if(a)c.push(new e.Paren(a));else return null}while(a);if(c.length>0)return new e.Expression(c)},mediaFeatures:function(){var a,b=[];do if(a=t(this.mediaFeature)){b.push(a);if(!t(","))break}else if(a=t(this.entities.variable)){b.push(a);if(!t(","))break}while(a);return b.length>0?b:null},media:function(){var a,b;if(t(/^@media/)){a=t(this.mediaFeatures);if(b=t(this.block))return new e.Media(b,a)}},directive:function(){var a,c,d,g,h,i;if(b.charAt(f)!=="@")return;if(c=t(this["import"])||t(this.media))return c;if(a=t(/^@page|@keyframes/)||t(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){g=(t(/^[^{]+/)||"").trim();if(d=t(this.block))return new e.Directive(a+" "+g,d)}else if(a=t(/^@[-a-z]+/))if(a==="@font-face"){if(d=t(this.block))return new e.Directive(a,d)}else if((c=t(this.entity))&&t(";"))return new e.Directive(a,c)},font:function(){var a=[],b=[],c,d,f,g;while(g=t(this.shorthand)||t(this.entity))b.push(g);a.push(new e.Expression(b));if(t(","))while(g=t(this.expression)){a.push(g);if(!t(","))break}return new e.Value(a)},value:function(){var a,b=[],c;while(a=t(this.expression)){b.push(a);if(!t(","))break}if(b.length>0)return new e.Value(b)},important:function(){if(b.charAt(f)==="!")return t(/^! *important/)},sub:function(){var a;if(t("(")&&(a=t(this.expression))&&t(")"))return a},multiplication:function(){var a,b,c,d;if(a=t(this.operand)){while(!w(/^\/\*/)&&(c=t("/")||t("*"))&&(b=t(this.operand)))d=new e.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,c,d,g;if(a=t(this.multiplication)){while((d=t(/^[-+]\s+/)||b.charAt(f-1)!=" "&&(t("+")||t("-")))&&(c=t(this.multiplication)))g=new e.Operation(d,[g||a,c]);return g||a}},conditions:function(){var a,b,c=f,d;if(a=t(this.condition)){while(t(",")&&(b=t(this.condition)))d=new e.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,g=f,h=!1;t(/^not/)&&(h=!0),u("(");if(a=t(this.addition)||t(this.entities.keyword)||t(this.entities.quoted))return(d=t(/^(?:>=|=<|[<=>])/))?(b=t(this.addition)||t(this.entities.keyword)||t(this.entities.quoted))?c=new e.Condition(d,a,b,g,h):v("expected expression"):c=new e.Condition("=",a,new e.Keyword("true"),g,h),u(")"),t(/^and/)?new e.Condition("and",c,t(this.condition)):c},operand:function(){var a,c=b.charAt(f+1);b.charAt(f)==="-"&&(c==="@"||c==="(")&&(a=t("-"));var d=t(this.sub)||t(this.entities.dimension)||t(this.entities.color)||t(this.entities.variable)||t(this.entities.call);return a?new e.Operation("*",[new e.Dimension(-1),d]):d},expression:function(){var a,b,c=[],d;while(a=t(this.addition)||t(this.entity))c.push(a);if(c.length>0)return new e.Expression(c)},property:function(){var a;if(a=t(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){!/^([a-z]+:)?\//.test(a)&&b.length>0&&(a=b[0]+a),n({href:a,title:a,type:d.mime},function(e){e&&typeof d.errback=="function"?d.errback.call(null,a,b,c,d):c.apply(null,arguments)},!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else if(c.compare)e=c.compare(b);else throw{type:"Type",message:"Unable to perform comparison",index:d};switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d,e){var f=this;this.index=e,this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(b,c){b&&(b.index=e),f.root=c||new a.Ruleset([],[])})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e1){var d=new a.Element("&",null,0),e=[new a.Selector([d])];c=new a.Ruleset(e,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(a.length===0)return[];if(a.length===1)return a[0];var b=[],c=this.permute(a.slice(1));for(var d=0;d0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;gthis.params.length)return!1;if(this.required>0&&c>this.params.length)return!1}if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;fe.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":",\n"),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.file=c},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{type:"Name",message:"variable "+e+" is undefined",filename:this.file,index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var f=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||f?"development":"production"),d.async=!1,d.poll=d.poll||(f?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&m(function(a,b,c,d,e){b&&p(b.toCSS(),d,e.lastModified)})},d.poll)):d.optimization=3;var g;try{g=typeof a.localStorage=="undefined"?null:a.localStorage}catch(h){g=null}var i=document.getElementsByTagName("link"),j=/^text\/(x-)?less$/;d.sheets=[];for(var k=0;k>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - if (!env.contents) { env.contents={}; } // env.contents must be passed arround with top env - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = path in that.files; - - that.files[path] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(e) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - } - if (level > 0) { - error = new(LessError)({ - index: i, - type: 'Parse', - message: "missing closing `}`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args = [], arg, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - while (arg = $(this.expression)) { - value = arg; - name = null; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - name = val.name; - } else { - throw new(Error)("Expected value"); - } - } - } - } - - args.push({ name: name, value: value }); - - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - } - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^)@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(') && (v = ($(this.entities.variableCurly) || $(this.entities.variable))) && $(')')) { - e = new(tree.Paren)(v); - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - // depreciated, will be removed soon - if ($('(')) { - sel = $(this.entity); - expect(')'); - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[A-Za-z][A-Za-z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - // __ Now using the hack of passing a ref to top parser's content cache in the 1st arg. __ - loadStyleSheet({ href: path, title: path, type: env.mime, contents: env.contents }, function (e) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.apply(null, arguments); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) - * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(number(n).toFixed(fraction), n.unit); - } else if (typeof(n) === 'number') { - return n.toFixed(fraction); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var blockIndex = env.mediaBlocks.length; - env.mediaPath.push(this); - env.mediaBlocks.push(this); - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaBlocks[blockIndex] = media; - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index, filename: this.filename }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []), varargs, arg; - - for (var i = 0, val, name; i < this.params.length; i++) { - arg = args && args[i] - - if (arg && arg.name) { - frame.rules.unshift(new(tree.Rule)(arg.name, arg.value.eval(env))); - args.splice(i, 1); - i--; - continue; - } - - if (name = this.params[i].name) { - if (this.params[i].variadic && args) { - varargs = []; - for (var j = i; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else if (val = (arg && arg.value) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules, start; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push((args[i] && args[i].value) || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules = []; - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - rules = rules.concat(ruleset.rules[i].eval(env)); - } else { - rules.push(ruleset.rules[i]); - } - } - ruleset.rules = rules; - rules = []; - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = rules.concat(ruleset.rules[i].eval(env)); - } else { - rules.push(ruleset.rules[i]); - } - } - ruleset.rules = rules; - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive) || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - this.value = val; - this.paths = paths; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx); - - // Add the base path if the URL is relative and we are in the browser - if (typeof window !== 'undefined' && typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value) && this.paths.length > 0) { - val.value = this.paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - - return new(tree.URL)(val, this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:"' + ctx.debugInfo.fileName + '";}line{font-family:"' + ctx.debugInfo.lineNumber + '";}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -// -// Watch mode -// -less.watch = function () { return this.watchMode = true }; -less.unwatch = function () { return this.watchMode = false }; - -if (less.env === 'development') { - less.optimization = 0; - - if (/!watch/.test(location.hash)) { - less.watch(); - } - var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); - if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; - } - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); -} else { - less.optimization = 3; -} - -var cache; - -try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; -} catch (_) { - cache = null; -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)({ - filename: document.location.href.replace(/#.*$/, ''), - dumpLineNumbers: less.dumpLineNumbers - }).parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var contents = sheet.contents || {}; // Passing a ref to top importing parser content cache trough 'sheet' arg. - var url = window.location.href.replace(/[#?].*$/, ''); - var href = sheet.href.replace(/\?.*$/, ''); - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - - // Stylesheets in IE don't always return the full path - if (! /^[a-z-]+:/.test(href)) { - if (href.charAt(0) == "/") { - href = window.location.protocol + "//" + window.location.host + href; - } else { - href = url.slice(0, url.lastIndexOf('/') + 1) + href; - } - } - xhr(sheet.href, sheet.type, function (data, lastModified) { - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }); - } else { - // Use remote copy (re-parse) - try { - contents[href] = data; // Updating top importing parser content cache - new(less.Parser)({ - optimization: less.optimization, - paths: [href.replace(/[\w\.-]+$/, '')], - mime: sheet.type, - filename: href, - 'contents': contents, // Passing top importing parser content cache ref down. - dumpLineNumbers: less.dumpLineNumbers - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\?.*$/, '' ) // Remove query - .replace(/\.[^\.\/]+$/, '' ) // Remove file extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - if( sheet.media ){ css.media = sheet.media; } - css.id = id; - var nextEl = sheet && sheet.nextSibling || null; - document.getElementsByTagName('head')[0].insertBefore(css, nextEl); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - var filenameNoPath = filename.match(/([^\/]+)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define("less", [], function () { return less; } ); -} -})(window); diff --git a/dist/less-1.3.1.min.js b/dist/less-1.3.1.min.js deleted file mode 100644 index 4043d44f3..000000000 --- a/dist/less-1.3.1.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.3.1 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(e,t){function n(t){return e.less[t.split("/")[1]]}function h(){var e=document.getElementsByTagName("style");for(var t=0;t0?r.firstChild.nodeValue!==e.nodeValue&&r.replaceChild(e,r.firstChild):r.appendChild(e)})(document.createTextNode(e));if(n&&u){w("saving "+i+" to cache.");try{u.setItem(i,e),u.setItem(i+":timestamp",n)}catch(a){w("failed to save")}}}function g(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var o=y(),u=s?r.fileAsync:r.async;typeof o.overrideMimeType=="function"&&o.overrideMimeType("text/css"),o.open("GET",e,u),o.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),o.send(null),s&&!r.fileAsync?o.status===0||o.status>=200&&o.status<300?n(o.responseText):i(o.status,e):u?o.onreadystatechange=function(){o.readyState==4&&a(o,n,i)}:a(o,n,i)}function y(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return w("browser doesn't support AJAX."),null}}function b(e){return e&&e.parentNode.removeChild(e)}function w(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function E(e,t){var n="less-error-message:"+v(t),i='
  • {content}
  • ',s=document.createElement("div"),o,u,a=[],f=e.filename||t,l=f.match(/([^\/]+)$/)[1];s.id=n,s.className="less-error-message",u="

    "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+l+" ";var c=function(e,t,n){e.extract[t]&&a.push(i.replace(/\{line\}/,parseInt(e.line)+(t-1)).replace(/\{class\}/,n).replace(/\{content\}/,e.extract[t]))};e.stack?u+="
    "+e.stack.split("\n").slice(1).join("
    "):e.extract&&(c(e,0,""),c(e,1,"line"),c(e,2,""),u+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+a.join("")+"
    "),s.innerHTML=u,m([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),s.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(o=setInterval(function(){document.body&&(document.getElementById(n)?document.body.replaceChild(s,document.getElementById(n)):document.body.insertBefore(s,document.body.firstChild),clearInterval(o))},10))}Array.isArray||(Array.isArray=function(e){return Object.prototype.toString.call(e)==="[object Array]"||e instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(e,t){var n=this.length>>>0;for(var r=0;r>>0,n=new Array(t),r=arguments[1];for(var i=0;i>>0,n=0;if(t===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n=t)return-1;n<0&&(n+=t);for(;nh&&(c[u]=c[u].slice(o-h),h=o)}function w(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function E(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,b();else{b();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return S(r),typeof t=="string"?t:t.length===1?t[0]:t}function S(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function L(e){return r.mode==="browser"||r.mode==="rhino"?e.filename:n("path").resolve(e.filename)}function A(e,t,n){return{lineNumber:k(e,t).line+1,fileName:L(n)}}function O(e,t){var n=C(e,t),r=k(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&k(e.call,n).line+1,this.callExtract=o[k(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this,t=t||{};t.contents||(t.contents={});var v=function(){},m=this.imports={paths:t&&t.paths||[],queue:[],files:{},contents:t.contents,mime:t&&t.mime,error:null,push:function(e,n){var i=this;this.queue.push(e),r.Parser.importer(e,this.paths,function(t,r){i.queue.splice(i.queue.indexOf(e),1);var s=e in i.files;i.files[e]=r,t&&!i.error&&(i.error=t),n(t,r,s),i.queue.length===0&&v(t)},t)}};return this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,p={imports:m,parse:function(e,a){var f,d,m,g,y,b,w=[],S,x=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0&&(x=new O({index:c,type:"Parse",message:"missing closing `}`",filename:t.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(x)return a(x);try{f=new i.Ruleset([],E(this.parsers.primary)),f.root=!0}catch(T){return a(new O(T,t))}f.toCSS=function(e){var s,o,u;return function(s,o){var u=[],a;s=s||{},typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),u=[new i.Ruleset(null,o)]);try{var f=e.call(this,{frames:u}).toCSS([],{compress:s.compress||!1,dumpLineNumbers:t.dumpLineNumbers})}catch(l){throw new O(l,t)}if(a=p.imports.error)throw a instanceof O?a:new O(a,t);return s.yuicompress&&r.mode==="node"?n("./cssmin").compressor.cssmin(f):s.compress?f.replace(/(\s)+/g,"$1"):f}}(f.eval);if(o=0&&s.charAt(N)!=="\n";N--)C++;x={type:"Parse",message:"Syntax Error on line "+y,index:o,filename:t.filename,line:y,column:C,extract:[b[y-2],b[y-1],b[y]]}}this.imports.queue.length>0?v=function(e){e?a(e):a(null,f)}:a(x,f)},parsers:{primary:function(){var e,t=[];while((e=E(this.mixin.definition)||E(this.rule)||E(this.ruleset)||E(this.mixin.call)||E(this.comment)||E(this.directive))||E(/^[\s\n]+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(E(/^\/\/.*/),!0);if(e=E(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=='"'&&s.charAt(t)!=="'")return;n&&E("~");if(e=E(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],n)},keyword:function(){var e;if(e=E(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=E(this.alpha);if(typeof s!="undefined")return s}E("("),r=E(this.entities.arguments);if(!E(")"))return;if(e)return new i.Call(e,r,a,t.filename)},arguments:function(){var e=[],t;while(t=E(this.entities.assignment)||E(this.expression)){e.push(t);if(!E(","))break}return e},literal:function(){return E(this.entities.ratio)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.quoted)},assignment:function(){var e,t;if((e=E(/^\w+(?=\s?=)/i))&&E("=")&&(t=E(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!E(/^url\(/))return;return e=E(this.entities.quoted)||E(this.entities.variable)||E(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",x(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),m.paths)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=E(/^@@?[\w-]+/)))return new i.Variable(e,n,t.filename)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=E(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.filename)},color:function(){var e;if(s.charAt(o)==="#"&&(e=E(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<45||t===47)return;if(e=E(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/))return new i.Dimension(e[1],e[2])},ratio:function(){var e,t=s.charCodeAt(o);if(t>57||t<48)return;if(e=E(/^(\d+\/\d+)/))return new i.Ratio(e[1])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&E("~");if(e=E(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=E(/^(@[\w-]+)\s*:/)))return e[1]},shorthand:function(){var e,t;if(!N(/^[@\w.%-]+\/[@\w.-]+/))return;g();if((e=E(this.entity))&&E("/")&&(t=E(this.entity)))return new i.Shorthand(e,t);y()},mixin:{call:function(){var e=[],n,r,u=[],a,f=o,l=s.charAt(o),c,h,p=!1;if(l!=="."&&l!=="#")return;g();while(n=E(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=E(">");if(E("(")){while(a=E(this.expression)){h=a,c=null;if(a.value.length==1){var d=a.value[0];if(d instanceof i.Variable&&E(":")){if(!(h=E(this.expression)))throw new Error("Expected value");c=d.name}}u.push({name:c,value:h});if(!E(","))break}if(!E(")"))throw new Error("Expected )")}E(this.important)&&(p=!0);if(e.length>0&&(E(";")||N("}")))return new i.mixin.Call(e,u,f,t.filename,p);y()},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||N(/^[^{]*(;|})/))return;g();if(n=E(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];do{if(s.charAt(o)==="."&&E(/^\.{3}/)){c=!0;break}if(!(u=E(this.entities.variable)||E(this.entities.literal)||E(this.entities.keyword)))break;if(u instanceof i.Variable)if(E(":"))a=x(this.expression,"expected expression"),t.push({name:u.name,value:a});else{if(E(/^\.{3}/)){t.push({name:u.name,variadic:!0}),c=!0;break}t.push({name:u.name})}else t.push({value:u})}while(E(","));E(")")||(l=o,y()),E(/^when/)&&(f=x(this.conditions,"expected condition")),r=E(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);y()}}},entity:function(){return E(this.entities.literal)||E(this.entities.variable)||E(this.entities.url)||E(this.entities.call)||E(this.entities.keyword)||E(this.entities.javascript)||E(this.comment)},end:function(){return E(";")||N("}")},alpha:function(){var e;if(!E(/^\(opacity=/i))return;if(e=E(/^\d+/)||E(this.entities.variable))return x(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=E(this.combinator),e=E(/^(?:\d+\.\d+|\d+)%/)||E(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||E("*")||E("&")||E(this.attribute)||E(/^\([^)@]+\)/)||E(/^[\.#](?=@)/)||E(this.entities.variableCurly),e||E("(")&&(r=E(this.entities.variableCurly)||E(this.entities.variable))&&E(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e,t=s.charAt(o);if(t===">"||t==="+"||t==="~"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(t)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u;if(E("("))return e=E(this.entity),x(")"),new i.Selector([new i.Element("",e,o)]);while(t=E(this.element)){r=s.charAt(o),n.push(t);if(r==="{"||r==="}"||r===";"||r===",")break}if(n.length>0)return new i.Selector(n)},tag:function(){return E(/^[A-Za-z][A-Za-z-]*[0-9]?/)||E("*")},attribute:function(){var e="",t,n,r;if(!E("["))return;if(t=E(/^(?:[_A-Za-z0-9-]|\\.)+/)||E(this.entities.quoted))(r=E(/^[|~*$^]?=/))&&(n=E(this.entities.quoted)||E(/^[\w-]+/))?e=[t,r,n.toCSS?n.toCSS():n].join(""):e=t;if(!E("]"))return;if(e)return"["+e+"]"},block:function(){var e;if(E("{")&&(e=E(this.primary))&&E("}"))return e},ruleset:function(){var e=[],n,r,u,a;g(),t.dumpLineNumbers&&(a=A(o,s,t));while(n=E(this.selector)){e.push(n),E(this.comment);if(!E(","))break;E(this.comment)}if(e.length>0&&(r=E(this.block))){var f=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(f.debugInfo=a),f}l=o,y()},rule:function(){var e,t,n=s.charAt(o),r,a;g();if(n==="."||n==="#"||n==="&")return;if(e=E(this.variable)||E(this.property)){e.charAt(0)!="@"&&(a=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))?(o+=a[0].length-1,t=new i.Anonymous(a[1])):e==="font"?t=E(this.font):t=E(this.value),r=E(this.important);if(t&&E(this.end))return new i.Rule(e,t,r,f);l=o,y()}},"import":function(){var e,t,n=o;g();var r=E(/^@import(?:-(once))?\s+/);if(r&&(e=E(this.entities.quoted)||E(this.entities.url))){t=E(this.mediaFeatures);if(E(";"))return new i.Import(e,m,t,r[1]==="once",n)}y()},mediaFeature:function(){var e,t,n=[];do if(e=E(this.entities.keyword))n.push(e);else if(E("(")){t=E(this.property),e=E(this.entity);if(!E(")"))return null;if(t&&e)n.push(new i.Paren(new i.Rule(t,e,null,o,!0)));else{if(!e)return null;n.push(new i.Paren(e))}}while(e);if(n.length>0)return new i.Expression(n)},mediaFeatures:function(){var e,t=[];do if(e=E(this.mediaFeature)){t.push(e);if(!E(","))break}else if(e=E(this.entities.variable)){t.push(e);if(!E(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=A(o,s,t));if(E(/^@media/)){e=E(this.mediaFeatures);if(n=E(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,t,n,r,u,a,f,l,c;if(s.charAt(o)!=="@")return;if(t=E(this["import"])||E(this.media))return t;g(),e=E(/^@[a-z-]+/),f=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(f="@"+e.slice(e.indexOf("-",2)+1));switch(f){case"@font-face":l=!0;break;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":l=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":l=!0,c=!0}c&&(e+=" "+(E(/^[^{]+/)||"").trim());if(l){if(n=E(this.block))return new i.Directive(e,n)}else if((t=E(this.entity))&&E(";"))return new i.Directive(e,t);y()},font:function(){var e=[],t=[],n,r,s,o;while(o=E(this.shorthand)||E(this.entity))t.push(o);e.push(new i.Expression(t));if(E(","))while(o=E(this.expression)){e.push(o);if(!E(","))break}return new i.Value(e)},value:function(){var e,t=[],n;while(e=E(this.expression)){t.push(e);if(!E(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return E(/^! *important/)},sub:function(){var e;if(E("(")&&(e=E(this.expression))&&E(")"))return e},multiplication:function(){var e,t,n,r;if(e=E(this.operand)){while(!N(/^\/\*/)&&(n=E("/")||E("*"))&&(t=E(this.operand)))r=new i.Operation(n,[r||e,t]);return r||e}},addition:function(){var e,t,n,r;if(e=E(this.multiplication)){while((n=E(/^[-+]\s+/)||!w(s.charAt(o-1))&&(E("+")||E("-")))&&(t=E(this.multiplication)))r=new i.Operation(n,[r||e,t]);return r||e}},conditions:function(){var e,t,n=o,r;if(e=E(this.condition)){while(E(",")&&(t=E(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;E(/^not/)&&(u=!0),x("(");if(e=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))return(r=E(/^(?:>=|=<|[<=>])/))?(t=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):T("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),x(")"),E(/^and/)?new i.Condition("and",n,E(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=E("-"));var n=E(this.sub)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.variable)||E(this.entities.call);return e?new i.Operation("*",[new i.Dimension(-1),n]):n},expression:function(){var e,t,n=[],r;while(e=E(this.addition)||E(this.entity))n.push(e);if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=E(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.length>0&&(e=t[0]+e),d({href:e,title:e,type:r.mime,contents:r.contents},function(i){i&&typeof r.errback=="function"?r.errback.call(null,e,t,n,r):n.apply(null,arguments)},!0)};(function(e){function t(t){return e.functions.hsla(t.h,t.s,t.l,t.a)}function n(t){if(t instanceof e.Dimension)return parseFloat(t.unit=="%"?t.value/100:t.value);if(typeof t=="number")return t;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function r(e){return Math.min(1,Math.max(0,e))}e.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(t,r,i,s){var o=[t,r,i].map(function(e){return n(e)}),s=n(s);return new e.Color(o,s)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,r,i){function u(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?o+(s-o)*e*6:e*2<1?s:e*3<2?o+(s-o)*(2/3-e)*6:o}e=n(e)%360/360,t=n(t),r=n(r),i=n(i);var s=r<=.5?r*(t+1):r+t-r*t,o=r*2-s;return this.rgba(u(e+1/3)*255,u(e)*255,u(e-1/3)*255,i)},hue:function(t){return new e.Dimension(Math.round(t.toHSL().h))},saturation:function(t){return new e.Dimension(Math.round(t.toHSL().s*100),"%")},lightness:function(t){return new e.Dimension(Math.round(t.toHSL().l*100),"%")},red:function(t){return new e.Dimension(t.rgb[0])},green:function(t){return new e.Dimension(t.rgb[1])},blue:function(t){return new e.Dimension(t.rgb[2])},alpha:function(t){return new e.Dimension(t.toHSL().a)},luma:function(t){return new e.Dimension(Math.round((.2126*(t.rgb[0]/255)+.7152*(t.rgb[1]/255)+.0722*(t.rgb[2]/255))*t.alpha*100),"%")},saturate:function(e,n){var i=e.toHSL();return i.s+=n.value/100,i.s=r(i.s),t(i)},desaturate:function(e,n){var i=e.toHSL();return i.s-=n.value/100,i.s=r(i.s),t(i)},lighten:function(e,n){var i=e.toHSL();return i.l+=n.value/100,i.l=r(i.l),t(i)},darken:function(e,n){var i=e.toHSL();return i.l-=n.value/100,i.l=r(i.l),t(i)},fadein:function(e,n){var i=e.toHSL();return i.a+=n.value/100,i.a=r(i.a),t(i)},fadeout:function(e,n){var i=e.toHSL();return i.a-=n.value/100,i.a=r(i.a),t(i)},fade:function(e,n){var i=e.toHSL();return i.a=n.value/100,i.a=r(i.a),t(i)},spin:function(e,n){var r=e.toHSL(),i=(r.h+n.value)%360;return r.h=i<0?360+i:i,t(r)},mix:function(t,n,r){r||(r=new e.Dimension(50));var i=r.value/100,s=i*2-1,o=t.toHSL().a-n.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[t.rgb[0]*u+n.rgb[0]*a,t.rgb[1]*u+n.rgb[1]*a,t.rgb[2]*u+n.rgb[2]*a],l=t.alpha*i+n.alpha*(1-i);return new e.Color(f,l)},greyscale:function(t){return this.desaturate(t,new e.Dimension(100))},contrast:function(e,t,n,r){return typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1)),typeof r=="undefined"?r=.43:r=r.value,(.2126*(e.rgb[0]/255)+.7152*(e.rgb[1]/255)+.0722*(e.rgb[2]/255))*e.alpha255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},operate:function(t,n){var r=[];n instanceof e.Color||(n=n.toColor());for(var i=0;i<3;i++)r[i]=e.operate(t,this.rgb[i],n.rgb[i]);return new e.Color(r,this.alpha+n.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t, -n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype.eval=function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}(n("../tree")),function(e){e.Dimension=function(e,t){this.value=parseFloat(e),this.unit=t||null},e.Dimension.prototype={eval:function(){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(){var e=this.value+this.unit;return e},operate:function(t,n){return new e.Dimension(e.operate(t,this.value,n.value),this.unit||n.unit)},compare:function(t){return t instanceof e.Dimension?t.value>this.value?-1:t.value":e.compress?">":" > "}[this.value]}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={eval:function(t){return this.value.length>1?new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?this.value[0].eval(t):this},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")}}}(n("../tree")),function(e){e.Import=function(t,n,r,i,s){var o=this;this.once=i,this.index=s,this._path=t,this.features=r&&new e.Value(r),t instanceof e.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(t.value)?t.value:t.value+".less":this.path=t.value.value||t.value,this.css=/css(\?.*)?$/.test(this.path),this.css||n.push(this.path,function(t,n,r){t&&(t.index=s),r&&o.once&&(o.skip=r),o.root=n||new e.Ruleset([],[])})},e.Import.prototype={toCSS:function(e){var t=this.features?" "+this.features.toCSS(e):"";return this.css?"@import "+this._path.toCSS()+t+";\n":""},eval:function(t){var n,r=this.features&&this.features.eval(t);if(this.skip)return[];if(this.css)return this;n=new e.Ruleset([],this.root.rules.slice(0));for(var i=0;i1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){n=this.arguments&&this.arguments.map(function(t){return{name:t.name,value:t.value.eval(e)}});for(var o=0;othis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}if(this.condition&&!this.condition.eval({frames:[this.evalParams(t,e)].concat(t.frames)}))return!1;r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t,n){var r=[],i=[],s=[],o=[],u=[],a,f,l;this.root||this.joinSelectors(u,t,this.selectors);for(var c=0;c0){f=e.debugInfo(n,this),a=u.map(function(e){return e.map(function(e){return e.toCSS(n)}).join("").trim()}).join(n.compress?",":",\n");for(var c=i.length-1;c>=0;c--)s.indexOf(i[c])===-1&&s.unshift(i[c]);i=s,r.push(f+a+(n.compress?"{":" {\n ")+i.join(n.compress?"":"\n ")+(n.compress?"}":"\n}\n"))}return r.push(o),r.join("")+(n.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0)),v=!1):d=new e.Selector([]),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t)):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e){this.elements=e},e.Selector.prototype.match=function(e){var t=this.elements.length,n=e.elements.length,r=Math.min(t,n);if(t0&&(r.value=this.paths[0]+(r.value.charAt(0)==="/"?r.value.slice(1):r.value)),new t.URL(r,this.paths)}}}(n("../tree")),function(e){e.Value=function(e){this.value=e,this.is="value"},e.Value.prototype={eval:function(t){return this.value.length===1?this.value[0].eval(t):new e.Value(this.value.map(function(e){return e.eval(t)}))},toCSS:function(e){return this.value.map(function(t){return t.toCSS(e)}).join(e.compress?",":", ")}}}(n("../tree")),function(e){e.Variable=function(e,t,n){this.name=e,this.index=t,this.file=n},e.Variable.prototype={eval:function(t){var n,r,i=this.name;i.indexOf("@@")==0&&(i="@"+(new e.Variable(i.slice(1))).eval(t).value);if(n=e.find(t.frames,function(e){if(r=e.variable(i))return r.value.eval(t)}))return n;throw{type:"Name",message:"variable "+i+" is undefined",filename:this.file,index:this.index}}}}(n("../tree")),function(e){e.debugInfo=function(t,n){var r="";if(t.dumpLineNumbers&&!t.compress)switch(t.dumpLineNumbers){case"comments":r=e.debugInfo.asComment(n);break;case"mediaquery":r=e.debugInfo.asMediaQuery(n);break;case"all":r=e.debugInfo.asComment(n)+e.debugInfo.asMediaQuery(n)}return r},e.debugInfo.asComment=function(e){return"/* line "+e.debugInfo.lineNumber+", "+e.debugInfo.fileName+" */\n"},e.debugInfo.asMediaQuery=function(e){return'@media -sass-debug-info{filename{font-family:"'+e.debugInfo.fileName+'";}line{font-family:"'+e.debugInfo.lineNumber+'";}}\n'},e.find=function(e,t){for(var n=0,r;n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree"));var s=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||s?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(s?1e3:1500),r.watch=function(){return this.watchMode=!0},r.unwatch=function(){return this.watchMode=!1};if(r.env==="development"){r.optimization=0,/!watch/.test(location.hash)&&r.watch();var o=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);o&&(r.dumpLineNumbers=o[1]),r.watchTimer=setInterval(function(){r.watchMode&&p(function(e,t,n,r,i){t&&m(t.toCSS(),r,i.lastModified)})},r.poll)}else r.optimization=3;var u;try{u=typeof e.localStorage=="undefined"?null:e.localStorage}catch(a){u=null}var f=document.getElementsByTagName("link"),l=/^text\/(x-)?less$/;r.sheets=[];for(var c=0;c>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree, charset; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - // env.contents and files must be passed arround with top env - if (!env.contents) { env.contents = {}; } - env.rootpath = env.rootpath || ''; // env.rootpath must be initialized to '' if not provided - if (!env.files) { env.files = {}; } - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, fullPath) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = fullPath in that.files; - - that.files[fullPath] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(that.error) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error, env); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - e = error || e; - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.rootpath); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, argsSemiColon = [], argsComma = [], args, delim, arg, nameLoop, expressions, isSemiColonSeperated, expressionContainsNamed, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - expressions = []; - while (arg = $(this.expression)) { - nameLoop = null; - value = arg; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } - } - } - - expressions.push(value); - - argsComma.push({ name: nameLoop, value: value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name: name, value: value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - expect(')'); - } - - args = isSemiColonSeperated ? argsSemiColon : argsComma; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - params.push({ variadic: true }); - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',') || $(';')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.entities.variableCurly) || - $(this.entities.variable) || - $(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - // depreciated, will be removed soon - if ($('(')) { - sel = $(this.entity); - expect(')'); - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ - href: path, - title: path, - type: env.mime, - contents: env.contents, - files: env.files, - rootpath: env.rootpath, - entryPath: env.entryPath, - relativeUrls: env.relativeUrls }, - function (e, root, data, sheet, _, path) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.call(null, e, root, path); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) * - color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, n); - }, - ceil: function (n) { - return this._math(Math.ceil, n); - }, - floor: function (n) { - return this._math(Math.floor, n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit == '%') { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - if (other.unit && this.unit !== other.unit) { - return -1; - } - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index, rootpath) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - this.rootpath = rootpath; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /(\.[a-z]*$)|([\?;].*)$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css([\?;].*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - // Add the base path if the import is relative - if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) { - this._path.value = this.rootpath + this._path.value; - } - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex; - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(mixinFrames) - }); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames) - })) { return false } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { name: "OperationError", - message: "Operation on an invalid type" }; - } - - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Rule.prototype.makeImportant = function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(paths, env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; -}; -tree.Selector.prototype.match = function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen) - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, rootpath) { - this.value = val; - this.rootpath = rootpath; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) { - rootpath = this.rootpath; - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, this.rootpath); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -var cache = null; - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -var session_cache = ''; -less.modifyVars = function(record) { - var str = session_cache; - for (name in record) { - str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - new(less.Parser)().parse(str, function (e, root) { - createCSS(root.toCSS(), less.sheets[less.sheets.length - 1]); - }); -}; - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)({ - filename: document.location.href.replace(/#.*$/, ''), - dumpLineNumbers: less.dumpLineNumbers - }).parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/\/(?:[^\/\?#]+\/)|([\/\\]))?((?:[^\/\\\?#]+[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = baseUrlParts[1]; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace("\\", "/").split("/"); - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var contents = sheet.contents || {}; - var files = sheet.files || {}; - var hrefParts = extractUrlParts(sheet.href, window.location.href); - var href = hrefParts.url; - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - var rootpath; - - if (less.relativeUrls) { - if (less.rootpath) { - if (sheet.entryPath) { - rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, sheet.entryPath)).path; - } else { - rootpath = less.rootpath; - } - } else { - rootpath = hrefParts.path; - } - } else { - if (less.rootpath) { - rootpath = less.rootpath; - } else { - if (sheet.entryPath) { - rootpath = sheet.entryPath; - } else { - rootpath = hrefParts.path; - } - } - } - - xhr(href, sheet.type, function (data, lastModified) { - // Store data this session - session_cache += data.replace(/@import .+?;/ig, ''); - - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }, href); - } else { - // Use remote copy (re-parse) - try { - contents[href] = data; // Updating top importing parser content cache - new(less.Parser)({ - optimization: less.optimization, - paths: [hrefParts.path], - entryPath: sheet.entryPath || hrefParts.path, - mime: sheet.type, - filename: href, - rootpath: rootpath, - relativeUrls: sheet.relativeUrls, - contents: contents, // Passing top importing parser content cache ref down. - files: files, - dumpLineNumbers: less.dumpLineNumbers - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - if( sheet.media ){ css.media = sheet.media; } - css.id = id; - var nextEl = sheet && sheet.nextSibling || null; - (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define("less", [], function () { return less; } ); -} -})(window); diff --git a/dist/less-1.3.2.min.js b/dist/less-1.3.2.min.js deleted file mode 100644 index 5985526ee..000000000 --- a/dist/less-1.3.2.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.3.2 -// http://lesscss.org -// -// Copyright (c) 2009-2011, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,r,i){t&&S(t.toCSS(),r,i.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=t.contents||{},u=t.files||{},a=b(t.href,e.location.href),f=a.url,c=l&&l.getItem(f),h=l&&l.getItem(f+":timestamp"),p={css:c,timestamp:h},d;r.relativeUrls?r.rootpath?t.entryPath?d=b(r.rootpath+y(a.path,t.entryPath)).path:d=r.rootpath:d=a.path:r.rootpath?d=r.rootpath:t.entryPath?d=t.entryPath:d=a.path,x(f,t.type,function(e,l){v+=e.replace(/@import .+?;/ig,"");if(!i&&p&&l&&(new Date(l)).valueOf()===(new Date(p.timestamp)).valueOf())S(p.css,t),n(null,null,e,t,{local:!0,remaining:s},f);else try{o[f]=e,(new r.Parser({optimization:r.optimization,paths:[a.path],entryPath:t.entryPath||a.path,mime:t.type,filename:f,rootpath:d,relativeUrls:t.relativeUrls,contents:o,files:u,dumpLineNumbers:r.dumpLineNumbers})).parse(e,function(r,i){if(r)return k(r,f);try{n(r,i,e,t,{local:!1,lastModified:l,remaining:s},f),N(document.getElementById("less-error-message:"+E(f)))}catch(r){k(r,f)}})}catch(c){k(c,f)}},function(e,t){throw new Error("Couldn't load "+t+" ("+e+")")})}function E(e){return e.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r,i=t.href||"",s="less:"+(t.title||E(i));if((r=document.getElementById(s))===null){r=document.createElement("style"),r.type="text/css",t.media&&(r.media=t.media),r.id=s;var o=t&&t.nextSibling||null;(o||document.getElementsByTagName("head")[0]).parentNode.insertBefore(r,o)}if(r.styleSheet)try{r.styleSheet.cssText=e}catch(u){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(e){r.childNodes.length>0?r.firstChild.nodeValue!==e.nodeValue&&r.replaceChild(e,r.firstChild):r.appendChild(e)})(document.createTextNode(e));if(n&&l){C("saving "+i+" to cache.");try{l.setItem(i,e),l.setItem(i+":timestamp",n)}catch(u){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,t){var n="less-error-message:"+E(t),i='
  • {content}
  • ',s=document.createElement("div"),o,u,a=[],f=e.filename||t,l=f.match(/([^\/]+(\?.*)?)$/)[1];s.id=n,s.className="less-error-message",u="

    "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+l+" ";var c=function(e,t,n){e.extract[t]&&a.push(i.replace(/\{line\}/,parseInt(e.line)+(t-1)).replace(/\{class\}/,n).replace(/\{content\}/,e.extract[t]))};e.stack?u+="
    "+e.stack.split("\n").slice(1).join("
    "):e.extract&&(c(e,0,""),c(e,1,"line"),c(e,2,""),u+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+a.join("")+"
    "),s.innerHTML=u,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),s.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(o=setInterval(function(){document.body&&(document.getElementById(n)?document.body.replaceChild(s,document.getElementById(n)):document.body.insertBefore(s,document.body.firstChild),clearInterval(o))},10))}Array.isArray||(Array.isArray=function(e){return Object.prototype.toString.call(e)==="[object Array]"||e instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(e,t){var n=this.length>>>0;for(var r=0;r>>0,n=new Array(t),r=arguments[1];for(var i=0;i>>0,n=0;if(t===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n=t)return-1;n<0&&(n+=t);for(;nh&&(c[u]=c[u].slice(o-h),h=o)}function w(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function E(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,b();else{b();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return S(r),typeof t=="string"?t:t.length===1?t[0]:t}function S(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function L(e){return r.mode==="browser"||r.mode==="rhino"?e.filename:n("path").resolve(e.filename)}function A(e,t,n){return{lineNumber:k(e,t).line+1,fileName:L(n)}}function O(e,t){var n=C(e,t),r=k(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&k(e.call,n).line+1,this.callExtract=o[k(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this,t=t||{};t.contents||(t.contents={}),t.rootpath=t.rootpath||"",t.files||(t.files={});var v=function(){},m=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n){var i=this;this.queue.push(e),r.Parser.importer(e,this.paths,function(t,r,s){i.queue.splice(i.queue.indexOf(e),1);var o=s in i.files;i.files[s]=r,t&&!i.error&&(i.error=t),n(t,r,o),i.queue.length===0&&v(i.error)},t)}};return this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,p={imports:m,parse:function(e,a){var f,d,m,g,y,b,w=[],S,x=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(x)return a(x,t);try{f=new i.Ruleset([],E(this.parsers.primary)),f.root=!0}catch(T){return a(new O(T,t))}f.toCSS=function(e){var s,o,u;return function(s,o){var u=[],a;s=s||{},typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),u=[new i.Ruleset(null,o)]);try{var f=e.call(this,{frames:u}).toCSS([],{compress:s.compress||!1,dumpLineNumbers:t.dumpLineNumbers})}catch(l){throw new O(l,t)}if(a=p.imports.error)throw a instanceof O?a:new O(a,t);return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(f):s.compress?f.replace(/(\s)+/g,"$1"):f}}(f.eval);if(o=0&&s.charAt(N)!=="\n";N--)C++;x={type:"Parse",message:"Syntax Error on line "+y,index:o,filename:t.filename,line:y,column:C,extract:[b[y-2],b[y-1],b[y]]}}this.imports.queue.length>0?v=function(e){e=x||e,e?a(e):a(null,f)}:a(x,f)},parsers:{primary:function(){var e,t=[];while((e=E(this.mixin.definition)||E(this.rule)||E(this.ruleset)||E(this.mixin.call)||E(this.comment)||E(this.directive))||E(/^[\s\n]+/)||E(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(E(/^\/\/.*/),!0);if(e=E(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=='"'&&s.charAt(t)!=="'")return;n&&E("~");if(e=E(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],n)},keyword:function(){var e;if(e=E(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=E(this.alpha);if(typeof s!="undefined")return s}E("("),r=E(this.entities.arguments);if(!E(")"))return;if(e)return new i.Call(e,r,a,t.filename)},arguments:function(){var e=[],t;while(t=E(this.entities.assignment)||E(this.expression)){e.push(t);if(!E(","))break}return e},literal:function(){return E(this.entities.ratio)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.quoted)||E(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=E(/^\w+(?=\s?=)/i))&&E("=")&&(t=E(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!E(/^url\(/))return;return e=E(this.entities.quoted)||E(this.entities.variable)||E(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",x(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.rootpath)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=E(/^@@?[\w-]+/)))return new i.Variable(e,n,t.filename)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=E(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.filename)},color:function(){var e;if(s.charAt(o)==="#"&&(e=E(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=E(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/))return new i.Dimension(e[1],e[2])},ratio:function(){var e,t=s.charCodeAt(o);if(t>57||t<48)return;if(e=E(/^(\d+\/\d+)/))return new i.Ratio(e[1])},unicodeDescriptor:function(){var e;if(e=E(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&E("~");if(e=E(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=E(/^(@[\w-]+)\s*:/)))return e[1]},shorthand:function(){var e,t;if(!N(/^[@\w.%-]+\/[@\w.-]+/))return;g();if((e=E(this.entity))&&E("/")&&(t=E(this.entity)))return new i.Shorthand(e,t);y()},mixin:{call:function(){var e=[],n,r,u=[],a=[],f,l,c,h,p,d,v,m=o,b=s.charAt(o),w,S,C=!1;if(b!=="."&&b!=="#")return;g();while(n=E(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=E(">");if(E("(")){p=[];while(c=E(this.expression)){h=null,S=c;if(c.value.length==1){var k=c.value[0];k instanceof i.Variable&&E(":")&&(p.length>0&&(d&&T("Cannot mix ; and , as delimiter types"),v=!0),S=x(this.expression),h=w=k.name)}p.push(S),a.push({name:h,value:S});if(E(","))continue;if(E(";")||d)v&&T("Cannot mix ; and , as delimiter types"),d=!0,p.length>1&&(S=new i.Value(p)),u.push({name:w,value:S}),w=null,p=[],v=!1}x(")")}f=d?u:a,E(this.important)&&(C=!0);if(e.length>0&&(E(";")||N("}")))return new i.mixin.Call(e,f,m,t.filename,C);y()},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||N(/^[^{]*\}/))return;g();if(n=E(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];do{E(this.comment);if(s.charAt(o)==="."&&E(/^\.{3}/)){c=!0,t.push({variadic:!0});break}if(!(u=E(this.entities.variable)||E(this.entities.literal)||E(this.entities.keyword)))break;if(u instanceof i.Variable)if(E(":"))a=x(this.expression,"expected expression"),t.push({name:u.name,value:a});else{if(E(/^\.{3}/)){t.push({name:u.name,variadic:!0}),c=!0;break}t.push({name:u.name})}else t.push({value:u})}while(E(",")||E(";"));E(")")||(l=o,y()),E(this.comment),E(/^when/)&&(f=x(this.conditions,"expected condition")),r=E(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);y()}}},entity:function(){return E(this.entities.literal)||E(this.entities.variable)||E(this.entities.url)||E(this.entities.call)||E(this.entities.keyword)||E(this.entities.javascript)||E(this.comment)},end:function(){return E(";")||N("}")},alpha:function(){var e;if(!E(/^\(opacity=/i))return;if(e=E(/^\d+/)||E(this.entities.variable))return x(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=E(this.combinator),e=E(/^(?:\d+\.\d+|\d+)%/)||E(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||E("*")||E("&")||E(this.attribute)||E(/^\([^()@]+\)/)||E(/^[\.#](?=@)/)||E(this.entities.variableCurly),e||E("(")&&(r=E(this.entities.variableCurly)||E(this.entities.variable)||E(this.selector))&&E(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e,t=s.charAt(o);if(t===">"||t==="+"||t==="~"||t==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(t)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u;if(E("("))return e=E(this.entity),x(")"),new i.Selector([new i.Element("",e,o)]);while(t=E(this.element)){r=s.charAt(o),n.push(t);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n)},attribute:function(){var e="",t,n,r;if(!E("["))return;if(t=E(/^(?:[_A-Za-z0-9-]|\\.)+/)||E(this.entities.quoted))(r=E(/^[|~*$^]?=/))&&(n=E(this.entities.quoted)||E(/^[\w-]+/))?e=[t,r,n.toCSS?n.toCSS():n].join(""):e=t;if(!E("]"))return;if(e)return"["+e+"]"},block:function(){var e;if(E("{")&&(e=E(this.primary))&&E("}"))return e},ruleset:function(){var e=[],n,r,u,a;g(),t.dumpLineNumbers&&(a=A(o,s,t));while(n=E(this.selector)){e.push(n),E(this.comment);if(!E(","))break;E(this.comment)}if(e.length>0&&(r=E(this.block))){var f=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(f.debugInfo=a),f}l=o,y()},rule:function(){var e,t,n=s.charAt(o),r,a;g();if(n==="."||n==="#"||n==="&")return;if(e=E(this.variable)||E(this.property)){e.charAt(0)!="@"&&(a=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))?(o+=a[0].length-1,t=new i.Anonymous(a[1])):e==="font"?t=E(this.font):t=E(this.value),r=E(this.important);if(t&&E(this.end))return new i.Rule(e,t,r,f);l=o,y()}},"import":function(){var e,n,r=o;g();var s=E(/^@import(?:-(once))?\s+/);if(s&&(e=E(this.entities.quoted)||E(this.entities.url))){n=E(this.mediaFeatures);if(E(";"))return new i.Import(e,m,n,s[1]==="once",r,t.rootpath)}y()},mediaFeature:function(){var e,t,n=[];do if(e=E(this.entities.keyword))n.push(e);else if(E("(")){t=E(this.property),e=E(this.entity);if(!E(")"))return null;if(t&&e)n.push(new i.Paren(new i.Rule(t,e,null,o,!0)));else{if(!e)return null;n.push(new i.Paren(e))}}while(e);if(n.length>0)return new i.Expression(n)},mediaFeatures:function(){var e,t=[];do if(e=E(this.mediaFeature)){t.push(e);if(!E(","))break}else if(e=E(this.entities.variable)){t.push(e);if(!E(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=A(o,s,t));if(E(/^@media/)){e=E(this.mediaFeatures);if(n=E(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=E(this["import"])||E(this.media))return n;g(),e=E(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;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":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(E(/^[^{]+/)||"").trim());if(c){if(r=E(this.block))return new i.Directive(e,r)}else if((n=p?E(this.expression):E(this.entity))&&E(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=A(o,s,t)),d}y()},font:function(){var e=[],t=[],n,r,s,o;while(o=E(this.shorthand)||E(this.entity))t.push(o);e.push(new i.Expression(t));if(E(","))while(o=E(this.expression)){e.push(o);if(!E(","))break}return new i.Value(e)},value:function(){var e,t=[],n;while(e=E(this.expression)){t.push(e);if(!E(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return E(/^! *important/)},sub:function(){var e;if(E("(")&&(e=E(this.expression))&&E(")"))return e},multiplication:function(){var e,t,n,r;if(e=E(this.operand)){while(!N(/^\/[*\/]/)&&(n=E("/")||E("*"))&&(t=E(this.operand)))r=new i.Operation(n,[r||e,t]);return r||e}},addition:function(){var e,t,n,r;if(e=E(this.multiplication)){while((n=E(/^[-+]\s+/)||!w(s.charAt(o-1))&&(E("+")||E("-")))&&(t=E(this.multiplication)))r=new i.Operation(n,[r||e,t]);return r||e}},conditions:function(){var e,t,n=o,r;if(e=E(this.condition)){while(E(",")&&(t=E(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;E(/^not/)&&(u=!0),x("(");if(e=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))return(r=E(/^(?:>=|=<|[<=>])/))?(t=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):T("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),x(")"),E(/^and/)?new i.Condition("and",n,E(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=E("-"));var n=E(this.sub)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.variable)||E(this.entities.call);return e?new i.Operation("*",[new i.Dimension(-1),n]):n},expression:function(){var e,t,n=[],r;while(e=E(this.addition)||E(this.entity))n.push(e);if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=E(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.length>0&&(e=t[0]+e),w({href:e,title:e,type:r.mime,contents:r.contents,files:r.files,rootpath:r.rootpath,entryPath:r.entryPath,relativeUrls:r.relativeUrls},function(e,i,s,o,u,a){e&&typeof r.errback=="function"?r.errback.call(null,a,t,n,r):n.call(null,e,i,a)},!0)};(function(e){function t(t){return e.functions.hsla(t.h,t.s,t.l,t.a)}function n(t,n){return t instanceof e.Dimension&&t.unit=="%"?parseFloat(t.value*n/100):r(t)}function r(t){if(t instanceof e.Dimension)return parseFloat(t.unit=="%"?t.value/100:t.value);if(typeof t=="number")return t;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function i(e){return Math.min(1,Math.max(0,e))}e.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(t,i,s,o){var u=[t,i,s].map(function(e){return n(e,256)});return o=r(o),new e.Color(u,o)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,i){function u(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?o+(s-o)*e*6:e*2<1?s:e*3<2?o+(s-o)*(2/3-e)*6:o}e=r(e)%360/360,t=r(t),n=r(n),i=r(i);var s=n<=.5?n*(t+1):n+t-n*t,o=n*2-s;return this.rgba(u(e+1/3)*255,u(e)*255,u(e-1/3)*255,i)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,i){e=r(e)%360/360*360,t=r(t),n=r(n),i=r(i);var s,o;s=Math.floor(e/60%6),o=e/60-s;var u=[n,n*(1-t),n*(1-o*t),n*(1-(1-o)*t)],a=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(u[a[s][0]]*255,u[a[s][1]]*255,u[a[s][2]]*255,i)},hue:function(t){return new e.Dimension(Math.round(t.toHSL().h))},saturation:function(t){return new e.Dimension(Math.round(t.toHSL().s*100),"%")},lightness:function(t){return new e.Dimension(Math.round(t.toHSL().l*100),"%")},red:function(t){return new e.Dimension(t.rgb[0])},green:function(t){return new e.Dimension(t.rgb[1])},blue:function(t){return new e.Dimension(t.rgb[2])},alpha:function(t){return new e.Dimension(t.toHSL().a)},luma:function(t){return new e.Dimension(Math.round((.2126*(t.rgb[0]/255)+.7152*(t.rgb[1]/255)+.0722*(t.rgb[2]/255))*t.alpha*100),"%")},saturate:function(e,n){var r=e.toHSL();return r.s+=n.value/100,r.s=i(r.s),t(r)},desaturate:function(e,n){var r=e.toHSL();return r.s-=n.value/100,r.s=i(r.s),t(r)},lighten:function(e,n){var r=e.toHSL();return r.l+=n.value/100,r.l=i(r.l),t(r)},darken:function(e,n){var r=e.toHSL();return r.l-=n.value/100,r.l=i(r.l),t(r)},fadein:function(e,n){var r=e.toHSL();return r.a+=n.value/100,r.a=i(r.a),t(r)},fadeout:function(e,n){var r=e.toHSL();return r.a-=n.value/100,r.a=i(r.a),t(r)},fade:function(e,n){var r=e.toHSL();return r.a=n.value/100,r.a=i(r.a),t(r)},spin:function(e,n){var r=e.toHSL(),i=(r.h+n.value)%360;return r.h=i<0?360+i:i,t(r)},mix:function(t,n,r){r||(r=new e.Dimension(50));var i=r.value/100,s=i*2-1,o=t.toHSL().a-n.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[t.rgb[0]*u+n.rgb[0]*a,t.rgb[1]*u+n.rgb[1]*a,t.rgb[2]*u+n.rgb[2]*a],l=t.alpha*i+n.alpha*(1-i);return new e.Color(f,l)},greyscale:function(t){return this.desaturate(t,new e.Dimension(100))},contrast:function(e,t,n,r){return typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1)),typeof r=="undefined"?r=.43:r=r.value,(.2126*(e.rgb[0]/255)+.7152*(e.rgb[1]/255)+.0722*(e.rgb[2]/255))*e.alpha255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},operate:function(t,n){var r=[];n instanceof e.Color||(n=n.toColor());for(var i=0;i<3;i++)r[i]=e.operate(t,this.rgb[i],n.rgb[i]);return new e.Color(r,this.alpha+n.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype.eval=function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}(n("../tree")),function(e){e.Dimension=function(e,t){this.value=parseFloat(e),this.unit=t||null},e.Dimension.prototype={eval:function(){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(){var e=this.value+this.unit;return e},operate:function(t,n){return new e.Dimension(e.operate(t,this.value,n.value),this.unit||n.unit)},compare:function(t){return t instanceof e.Dimension?t.value>this.value?-1:t.value":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={eval:function(t){return this.value.length>1?new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?this.value[0].eval(t):this},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")}}}(n("../tree")),function(e){e.Import=function(t,n,r,i,s,o){var u=this;this.once=i,this.index=s,this._path=t,this.features=r&&new e.Value(r),this.rootpath=o,t instanceof e.Quoted?this.path=/(\.[a-z]*$)|([\?;].*)$/.test(t.value)?t.value:t.value+".less":this.path=t.value.value||t.value,this.css=/css([\?;].*)?$/.test(this.path),this.css||n.push(this.path,function(t,n,r){t&&(t.index=s),r&&u.once&&(u.skip=r),u.root=n||new e.Ruleset([],[])})},e.Import.prototype={toCSS:function(e){var t=this.features?" "+this.features.toCSS(e):"";return this.css?(typeof this._path.value=="string"&&!/^(?:[a-z-]+:|\/)/.test(this._path.value)&&(this._path.value=this.rootpath+this._path.value),"@import "+this._path.toCSS()+t+";\n"):""},eval:function(t){var n,r=this.features&&this.features.eval(t);return this.skip?[]:this.css?this:(n=new e.Ruleset([],this.root.rules.slice(0)),n.evalImports(t),this.features?new e.Media(n.rules,this.features.value):n.rules)}}}(n("../tree")),function(e){e.JavaScript=function(e,t,n){this.escaped=n,this.expression=e,this.index=t},e.JavaScript.prototype={eval:function(t){var n,r=this,i={},s=this.expression.replace(/@\{([\w-]+)\}/g,function(n,i){return e.jsify((new e.Variable("@"+i,r.index)).eval(t))});try{s=new Function("return ("+s+")")}catch(o){throw{message:"JavaScript evaluation error: `"+s+"`",index:this.index}}for(var u in t.frames[0].variables())i[u.slice(1)]={value:t.frames[0].variables()[u].value,toJS:function(){return this.value.eval(t).toCSS()}};try{n=s.call(i)}catch(o){throw{message:"JavaScript evaluation error: '"+o.name+": "+o.message+"'",index:this.index}}return typeof n=="string"?new e.Quoted('"'+n+'"',n,this.escaped,this.index):Array.isArray(n)?new e.Anonymous(n.join(", ")):new e.Anonymous(n)}}}(n("../tree")),function(e){e.Keyword=function(e){this.value=e},e.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value},compare:function(t){return t instanceof e.Keyword?t.value===this.value?0:1:-1}},e.True=new e.Keyword("true"),e.False=new e.Keyword("false")}(n("../tree")),function(e){e.Media=function(t,n){var r=this.emptySelectors();this.features=new e.Value(n),this.ruleset=new e.Ruleset(r,t),this.ruleset.allowImports=!0},e.Media.prototype={toCSS:function(e,t){var n=this.features.toCSS(t);return this.ruleset.root=e.length===0||e[0].multiMedia,"@media "+n+(t.compress?"{":" {\n ")+this.ruleset.toCSS(e,t).trim().replace(/\n/g,"\n ")+(t.compress?"}":"\n}\n")},eval:function(t){t.mediaBlocks||(t.mediaBlocks=[],t.mediaPath=[]);var n=new e.Media([],[]);return this.debugInfo&&(this.ruleset.debugInfo=this.debugInfo,n.debugInfo=this.debugInfo),n.features=this.features.eval(t),t.mediaPath.push(n),t.mediaBlocks.push(n),t.frames.unshift(this.ruleset),n.ruleset=this.ruleset.eval(t),t.frames.shift(),t.mediaPath.pop(),t.mediaPath.length===0?n.evalTop(t):n.evalNested(t)},variable:function(t){return e.Ruleset.prototype.variable.call(this.ruleset,t)},find:function(){return e.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return e.Ruleset.prototype.rulesets.apply(this.ruleset)},emptySelectors:function(){var t=new e.Element("","&",0);return[new e.Selector([t])]},evalTop:function(t){var n=this;if(t.mediaBlocks.length>1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t,n){var r=[],i=[],s=[],o=[],u=[],a,f,l;this.root||this.joinSelectors(u,t,this.selectors);for(var c=0;c0){f=e.debugInfo(n,this),a=u.map(function(e){return e.map(function(e){return e.toCSS(n)}).join("").trim()}).join(n.compress?",":",\n");for(var c=i.length-1;c>=0;c--)s.indexOf(i[c])===-1&&s.unshift(i[c]);i=s,r.push(f+a+(n.compress?"{":" {\n ")+i.join(n.compress?"":"\n ")+(n.compress?"}":"\n}\n"))}return r.push(o),r.join("")+(n.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0)),v=!1):d=new e.Selector([]),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t)):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e){this.elements=e},e.Selector.prototype.match=function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree, charset; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - // env.contents and files must be passed arround with top env - if (!env.contents) { env.contents = {}; } - env.rootpath = env.rootpath || ''; // env.rootpath must be initialized to '' if not provided - if (!env.files) { env.files = {}; } - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, fullPath) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = fullPath in that.files; - - that.files[fullPath] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(that.error) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error, env); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - e = error || e; - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.rootpath); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, argsSemiColon = [], argsComma = [], args, delim, arg, nameLoop, expressions, isSemiColonSeperated, expressionContainsNamed, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - expressions = []; - while (arg = $(this.expression)) { - nameLoop = null; - value = arg; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } - } - } - - expressions.push(value); - - argsComma.push({ name: nameLoop, value: value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name: name, value: value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - expect(')'); - } - - args = isSemiColonSeperated ? argsSemiColon : argsComma; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - params.push({ variadic: true }); - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',') || $(';')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.entities.variableCurly) || - $(this.entities.variable) || - $(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - // depreciated, will be removed soon - if ($('(')) { - sel = $(this.entity); - if (!$(')')) { return null; } - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ - href: path, - title: path, - type: env.mime, - contents: env.contents, - files: env.files, - rootpath: env.rootpath, - entryPath: env.entryPath, - relativeUrls: env.relativeUrls }, - function (e, root, data, sheet, _, path) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.call(null, e, root, path); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) * - color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, n); - }, - ceil: function (n) { - return this._math(Math.ceil, n); - }, - floor: function (n) { - return this._math(Math.floor, n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit == '%') { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // When evaluating a function call, - // we either find the function in `tree.functions` [1], - // in which case we call it, passing the evaluated arguments, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }), - result; - - if (this.name in tree.functions) { // 1. - try { - result = tree.functions[this.name].apply(tree.functions, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - if (other.unit && this.unit !== other.unit) { - return -1; - } - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index, rootpath) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - this.rootpath = rootpath; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /(\.[a-z]*$)|([\?;].*)$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css([\?;].*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - // Add the base path if the import is relative - if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) { - this._path.value = this.rootpath + this._path.value; - } - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex; - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(mixinFrames) - }); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames) - })) { return false } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { name: "OperationError", - message: "Operation on an invalid type" }; - } - - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Rule.prototype.makeImportant = function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(paths, env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; -}; -tree.Selector.prototype.match = function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen) - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, rootpath) { - this.value = val; - this.rootpath = rootpath; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) { - rootpath = this.rootpath; - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, this.rootpath); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (root) { - createCSS(root.toCSS(), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -var cache = null; - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -var session_cache = ''; -less.modifyVars = function(record) { - var str = session_cache; - for (name in record) { - str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - new(less.Parser)().parse(str, function (e, root) { - createCSS(root.toCSS(), less.sheets[less.sheets.length - 1]); - }); -}; - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - new(less.Parser)({ - filename: document.location.href.replace(/#.*$/, ''), - dumpLineNumbers: less.dumpLineNumbers - }).parse(styles[i].innerHTML || '', function (e, tree) { - var css = tree.toCSS(); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/\/(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = baseUrlParts[1]; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace("\\", "/").split("/"); - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var contents = sheet.contents || {}; - var files = sheet.files || {}; - var hrefParts = extractUrlParts(sheet.href, window.location.href); - var href = hrefParts.url; - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - var rootpath; - - if (less.relativeUrls) { - if (less.rootpath) { - if (sheet.entryPath) { - rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, sheet.entryPath)).path; - } else { - rootpath = less.rootpath; - } - } else { - rootpath = hrefParts.path; - } - } else { - if (less.rootpath) { - rootpath = less.rootpath; - } else { - if (sheet.entryPath) { - rootpath = sheet.entryPath; - } else { - rootpath = hrefParts.path; - } - } - } - - xhr(href, sheet.type, function (data, lastModified) { - // Store data this session - session_cache += data.replace(/@import .+?;/ig, ''); - - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }, href); - } else { - // Use remote copy (re-parse) - try { - contents[href] = data; // Updating top importing parser content cache - new(less.Parser)({ - optimization: less.optimization, - paths: [hrefParts.path], - entryPath: sheet.entryPath || hrefParts.path, - mime: sheet.type, - filename: href, - rootpath: rootpath, - relativeUrls: sheet.relativeUrls, - contents: contents, // Passing top importing parser content cache ref down. - files: files, - dumpLineNumbers: less.dumpLineNumbers - }).parse(data, function (e, root) { - if (e) { return error(e, href) } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href); - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } catch (e) { - error(e, href); - } - }); - } catch (e) { - error(e, href); - } - } - }, function (status, url) { - throw new(Error)("Couldn't load " + url + " (" + status + ")"); - }); -} - -function extractId(href) { - return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - var css; - - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If the stylesheet doesn't exist, create a new node - if ((css = document.getElementById(id)) === null) { - css = document.createElement('style'); - css.type = 'text/css'; - if( sheet.media ){ css.media = sheet.media; } - css.id = id; - var nextEl = sheet && sheet.nextSibling || null; - (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); - } - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - (function (node) { - if (css.childNodes.length > 0) { - if (css.firstChild.nodeValue !== node.nodeValue) { - css.replaceChild(node, css.firstChild); - } - } else { - css.appendChild(node); - } - })(document.createTextNode(styles)); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, href) { - var id = 'less-error-message:' + extractId(href); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || href; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } else if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define("less", [], function () { return less; } ); -} -})(window); diff --git a/dist/less-1.3.3.min.js b/dist/less-1.3.3.min.js deleted file mode 100644 index 9b0fa6bae..000000000 --- a/dist/less-1.3.3.min.js +++ /dev/null @@ -1,9 +0,0 @@ -// -// LESS - Leaner CSS v1.3.3 -// http://lesscss.org -// -// Copyright (c) 2009-2013, Alexis Sellier -// Licensed under the Apache 2.0 License. -// -(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,r,i){t&&S(t.toCSS(),r,i.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=t.contents||{},u=t.files||{},a=b(t.href,e.location.href),f=a.url,c=l&&l.getItem(f),h=l&&l.getItem(f+":timestamp"),p={css:c,timestamp:h},d;r.relativeUrls?r.rootpath?t.entryPath?d=b(r.rootpath+y(a.path,t.entryPath)).path:d=r.rootpath:d=a.path:r.rootpath?d=r.rootpath:t.entryPath?d=t.entryPath:d=a.path,x(f,t.type,function(e,l){v+=e.replace(/@import .+?;/ig,"");if(!i&&p&&l&&(new Date(l)).valueOf()===(new Date(p.timestamp)).valueOf())S(p.css,t),n(null,null,e,t,{local:!0,remaining:s},f);else try{o[f]=e,(new r.Parser({optimization:r.optimization,paths:[a.path],entryPath:t.entryPath||a.path,mime:t.type,filename:f,rootpath:d,relativeUrls:t.relativeUrls,contents:o,files:u,dumpLineNumbers:r.dumpLineNumbers})).parse(e,function(r,i){if(r)return k(r,f);try{n(r,i,e,t,{local:!1,lastModified:l,remaining:s},f),N(document.getElementById("less-error-message:"+E(f)))}catch(r){k(r,f)}})}catch(c){k(c,f)}},function(e,t){throw new Error("Couldn't load "+t+" ("+e+")")})}function E(e){return e.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r,i=t.href||"",s="less:"+(t.title||E(i));if((r=document.getElementById(s))===null){r=document.createElement("style"),r.type="text/css",t.media&&(r.media=t.media),r.id=s;var o=t&&t.nextSibling||null;(o||document.getElementsByTagName("head")[0]).parentNode.insertBefore(r,o)}if(r.styleSheet)try{r.styleSheet.cssText=e}catch(u){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(e){r.childNodes.length>0?r.firstChild.nodeValue!==e.nodeValue&&r.replaceChild(e,r.firstChild):r.appendChild(e)})(document.createTextNode(e));if(n&&l){C("saving "+i+" to cache.");try{l.setItem(i,e),l.setItem(i+":timestamp",n)}catch(u){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,t){var n="less-error-message:"+E(t),i='
  • {content}
  • ',s=document.createElement("div"),o,u,a=[],f=e.filename||t,l=f.match(/([^\/]+(\?.*)?)$/)[1];s.id=n,s.className="less-error-message",u="

    "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+l+" ";var c=function(e,t,n){e.extract[t]&&a.push(i.replace(/\{line\}/,parseInt(e.line)+(t-1)).replace(/\{class\}/,n).replace(/\{content\}/,e.extract[t]))};e.stack?u+="
    "+e.stack.split("\n").slice(1).join("
    "):e.extract&&(c(e,0,""),c(e,1,"line"),c(e,2,""),u+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+a.join("")+"
    "),s.innerHTML=u,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),s.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(o=setInterval(function(){document.body&&(document.getElementById(n)?document.body.replaceChild(s,document.getElementById(n)):document.body.insertBefore(s,document.body.firstChild),clearInterval(o))},10))}Array.isArray||(Array.isArray=function(e){return Object.prototype.toString.call(e)==="[object Array]"||e instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(e,t){var n=this.length>>>0;for(var r=0;r>>0,n=new Array(t),r=arguments[1];for(var i=0;i>>0,n=0;if(t===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var r=arguments[1];else do{if(n in this){r=this[n++];break}if(++n>=t)throw new TypeError}while(!0);for(;n=t)return-1;n<0&&(n+=t);for(;nh&&(c[u]=c[u].slice(o-h),h=o)}function w(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function E(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,b();else{b();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return S(r),typeof t=="string"?t:t.length===1?t[0]:t}function S(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function L(e){return r.mode==="browser"||r.mode==="rhino"?e.filename:n("path").resolve(e.filename)}function A(e,t,n){return{lineNumber:k(e,t).line+1,fileName:L(n)}}function O(e,t){var n=C(e,t),r=k(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&k(e.call,n).line+1,this.callExtract=o[k(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this,t=t||{};t.contents||(t.contents={}),t.rootpath=t.rootpath||"",t.files||(t.files={});var v=function(){},m=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n){var i=this;this.queue.push(e),r.Parser.importer(e,this.paths,function(t,r,s){i.queue.splice(i.queue.indexOf(e),1);var o=s in i.files;i.files[s]=r,t&&!i.error&&(i.error=t),n(t,r,o),i.queue.length===0&&v(i.error)},t)}};return this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,p={imports:m,parse:function(e,a){var f,d,m,g,y,b,w=[],S,x=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(x)return a(x,t);try{f=new i.Ruleset([],E(this.parsers.primary)),f.root=!0}catch(T){return a(new O(T,t))}f.toCSS=function(e){var s,o,u;return function(s,o){var u=[],a;s=s||{},typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),u=[new i.Ruleset(null,o)]);try{var f=e.call(this,{frames:u}).toCSS([],{compress:s.compress||!1,dumpLineNumbers:t.dumpLineNumbers})}catch(l){throw new O(l,t)}if(a=p.imports.error)throw a instanceof O?a:new O(a,t);return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(f):s.compress?f.replace(/(\s)+/g,"$1"):f}}(f.eval);if(o=0&&s.charAt(N)!=="\n";N--)C++;x={type:"Parse",message:"Syntax Error on line "+y,index:o,filename:t.filename,line:y,column:C,extract:[b[y-2],b[y-1],b[y]]}}this.imports.queue.length>0?v=function(e){e=x||e,e?a(e):a(null,f)}:a(x,f)},parsers:{primary:function(){var e,t=[];while((e=E(this.mixin.definition)||E(this.rule)||E(this.ruleset)||E(this.mixin.call)||E(this.comment)||E(this.directive))||E(/^[\s\n]+/)||E(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(E(/^\/\/.*/),!0);if(e=E(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=='"'&&s.charAt(t)!=="'")return;n&&E("~");if(e=E(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],n)},keyword:function(){var e;if(e=E(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=E(this.alpha);if(typeof s!="undefined")return s}E("("),r=E(this.entities.arguments);if(!E(")"))return;if(e)return new i.Call(e,r,a,t.filename)},arguments:function(){var e=[],t;while(t=E(this.entities.assignment)||E(this.expression)){e.push(t);if(!E(","))break}return e},literal:function(){return E(this.entities.ratio)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.quoted)||E(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=E(/^\w+(?=\s?=)/i))&&E("=")&&(t=E(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!E(/^url\(/))return;return e=E(this.entities.quoted)||E(this.entities.variable)||E(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",x(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.rootpath)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=E(/^@@?[\w-]+/)))return new i.Variable(e,n,t.filename)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=E(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.filename)},color:function(){var e;if(s.charAt(o)==="#"&&(e=E(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=E(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/))return new i.Dimension(e[1],e[2])},ratio:function(){var e,t=s.charCodeAt(o);if(t>57||t<48)return;if(e=E(/^(\d+\/\d+)/))return new i.Ratio(e[1])},unicodeDescriptor:function(){var e;if(e=E(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&E("~");if(e=E(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=E(/^(@[\w-]+)\s*:/)))return e[1]},shorthand:function(){var e,t;if(!N(/^[@\w.%-]+\/[@\w.-]+/))return;g();if((e=E(this.entity))&&E("/")&&(t=E(this.entity)))return new i.Shorthand(e,t);y()},mixin:{call:function(){var e=[],n,r,u=[],a=[],f,l,c,h,p,d,v,m=o,b=s.charAt(o),w,S,C=!1;if(b!=="."&&b!=="#")return;g();while(n=E(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=E(">");if(E("(")){p=[];while(c=E(this.expression)){h=null,S=c;if(c.value.length==1){var k=c.value[0];k instanceof i.Variable&&E(":")&&(p.length>0&&(d&&T("Cannot mix ; and , as delimiter types"),v=!0),S=x(this.expression),h=w=k.name)}p.push(S),a.push({name:h,value:S});if(E(","))continue;if(E(";")||d)v&&T("Cannot mix ; and , as delimiter types"),d=!0,p.length>1&&(S=new i.Value(p)),u.push({name:w,value:S}),w=null,p=[],v=!1}x(")")}f=d?u:a,E(this.important)&&(C=!0);if(e.length>0&&(E(";")||N("}")))return new i.mixin.Call(e,f,m,t.filename,C);y()},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||N(/^[^{]*\}/))return;g();if(n=E(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];do{E(this.comment);if(s.charAt(o)==="."&&E(/^\.{3}/)){c=!0,t.push({variadic:!0});break}if(!(u=E(this.entities.variable)||E(this.entities.literal)||E(this.entities.keyword)))break;if(u instanceof i.Variable)if(E(":"))a=x(this.expression,"expected expression"),t.push({name:u.name,value:a});else{if(E(/^\.{3}/)){t.push({name:u.name,variadic:!0}),c=!0;break}t.push({name:u.name})}else t.push({value:u})}while(E(",")||E(";"));E(")")||(l=o,y()),E(this.comment),E(/^when/)&&(f=x(this.conditions,"expected condition")),r=E(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);y()}}},entity:function(){return E(this.entities.literal)||E(this.entities.variable)||E(this.entities.url)||E(this.entities.call)||E(this.entities.keyword)||E(this.entities.javascript)||E(this.comment)},end:function(){return E(";")||N("}")},alpha:function(){var e;if(!E(/^\(opacity=/i))return;if(e=E(/^\d+/)||E(this.entities.variable))return x(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=E(this.combinator),e=E(/^(?:\d+\.\d+|\d+)%/)||E(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||E("*")||E("&")||E(this.attribute)||E(/^\([^()@]+\)/)||E(/^[\.#](?=@)/)||E(this.entities.variableCurly),e||E("(")&&(r=E(this.entities.variableCurly)||E(this.entities.variable)||E(this.selector))&&E(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e,t=s.charAt(o);if(t===">"||t==="+"||t==="~"||t==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(t)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u;if(E("("))return e=E(this.entity),E(")")?new i.Selector([new i.Element("",e,o)]):null;while(t=E(this.element)){r=s.charAt(o),n.push(t);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n)},attribute:function(){var e="",t,n,r;if(!E("["))return;if(t=E(/^(?:[_A-Za-z0-9-]|\\.)+/)||E(this.entities.quoted))(r=E(/^[|~*$^]?=/))&&(n=E(this.entities.quoted)||E(/^[\w-]+/))?e=[t,r,n.toCSS?n.toCSS():n].join(""):e=t;if(!E("]"))return;if(e)return"["+e+"]"},block:function(){var e;if(E("{")&&(e=E(this.primary))&&E("}"))return e},ruleset:function(){var e=[],n,r,u,a;g(),t.dumpLineNumbers&&(a=A(o,s,t));while(n=E(this.selector)){e.push(n),E(this.comment);if(!E(","))break;E(this.comment)}if(e.length>0&&(r=E(this.block))){var f=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(f.debugInfo=a),f}l=o,y()},rule:function(){var e,t,n=s.charAt(o),r,a;g();if(n==="."||n==="#"||n==="&")return;if(e=E(this.variable)||E(this.property)){e.charAt(0)!="@"&&(a=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))?(o+=a[0].length-1,t=new i.Anonymous(a[1])):e==="font"?t=E(this.font):t=E(this.value),r=E(this.important);if(t&&E(this.end))return new i.Rule(e,t,r,f);l=o,y()}},"import":function(){var e,n,r=o;g();var s=E(/^@import(?:-(once))?\s+/);if(s&&(e=E(this.entities.quoted)||E(this.entities.url))){n=E(this.mediaFeatures);if(E(";"))return new i.Import(e,m,n,s[1]==="once",r,t.rootpath)}y()},mediaFeature:function(){var e,t,n=[];do if(e=E(this.entities.keyword))n.push(e);else if(E("(")){t=E(this.property),e=E(this.entity);if(!E(")"))return null;if(t&&e)n.push(new i.Paren(new i.Rule(t,e,null,o,!0)));else{if(!e)return null;n.push(new i.Paren(e))}}while(e);if(n.length>0)return new i.Expression(n)},mediaFeatures:function(){var e,t=[];do if(e=E(this.mediaFeature)){t.push(e);if(!E(","))break}else if(e=E(this.entities.variable)){t.push(e);if(!E(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=A(o,s,t));if(E(/^@media/)){e=E(this.mediaFeatures);if(n=E(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=E(this["import"])||E(this.media))return n;g(),e=E(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;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":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(E(/^[^{]+/)||"").trim());if(c){if(r=E(this.block))return new i.Directive(e,r)}else if((n=p?E(this.expression):E(this.entity))&&E(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=A(o,s,t)),d}y()},font:function(){var e=[],t=[],n,r,s,o;while(o=E(this.shorthand)||E(this.entity))t.push(o);e.push(new i.Expression(t));if(E(","))while(o=E(this.expression)){e.push(o);if(!E(","))break}return new i.Value(e)},value:function(){var e,t=[],n;while(e=E(this.expression)){t.push(e);if(!E(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return E(/^! *important/)},sub:function(){var e;if(E("(")&&(e=E(this.expression))&&E(")"))return e},multiplication:function(){var e,t,n,r;if(e=E(this.operand)){while(!N(/^\/[*\/]/)&&(n=E("/")||E("*"))&&(t=E(this.operand)))r=new i.Operation(n,[r||e,t]);return r||e}},addition:function(){var e,t,n,r;if(e=E(this.multiplication)){while((n=E(/^[-+]\s+/)||!w(s.charAt(o-1))&&(E("+")||E("-")))&&(t=E(this.multiplication)))r=new i.Operation(n,[r||e,t]);return r||e}},conditions:function(){var e,t,n=o,r;if(e=E(this.condition)){while(E(",")&&(t=E(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;E(/^not/)&&(u=!0),x("(");if(e=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))return(r=E(/^(?:>=|=<|[<=>])/))?(t=E(this.addition)||E(this.entities.keyword)||E(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):T("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),x(")"),E(/^and/)?new i.Condition("and",n,E(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=E("-"));var n=E(this.sub)||E(this.entities.dimension)||E(this.entities.color)||E(this.entities.variable)||E(this.entities.call);return e?new i.Operation("*",[new i.Dimension(-1),n]):n},expression:function(){var e,t,n=[],r;while(e=E(this.addition)||E(this.entity))n.push(e);if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=E(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.length>0&&(e=t[0]+e),w({href:e,title:e,type:r.mime,contents:r.contents,files:r.files,rootpath:r.rootpath,entryPath:r.entryPath,relativeUrls:r.relativeUrls},function(e,i,s,o,u,a){e&&typeof r.errback=="function"?r.errback.call(null,a,t,n,r):n.call(null,e,i,a)},!0)};(function(e){function t(t){return e.functions.hsla(t.h,t.s,t.l,t.a)}function n(t,n){return t instanceof e.Dimension&&t.unit=="%"?parseFloat(t.value*n/100):r(t)}function r(t){if(t instanceof e.Dimension)return parseFloat(t.unit=="%"?t.value/100:t.value);if(typeof t=="number")return t;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function i(e){return Math.min(1,Math.max(0,e))}e.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(t,i,s,o){var u=[t,i,s].map(function(e){return n(e,256)});return o=r(o),new e.Color(u,o)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,i){function u(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?o+(s-o)*e*6:e*2<1?s:e*3<2?o+(s-o)*(2/3-e)*6:o}e=r(e)%360/360,t=r(t),n=r(n),i=r(i);var s=n<=.5?n*(t+1):n+t-n*t,o=n*2-s;return this.rgba(u(e+1/3)*255,u(e)*255,u(e-1/3)*255,i)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,i){e=r(e)%360/360*360,t=r(t),n=r(n),i=r(i);var s,o;s=Math.floor(e/60%6),o=e/60-s;var u=[n,n*(1-t),n*(1-o*t),n*(1-(1-o)*t)],a=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(u[a[s][0]]*255,u[a[s][1]]*255,u[a[s][2]]*255,i)},hue:function(t){return new e.Dimension(Math.round(t.toHSL().h))},saturation:function(t){return new e.Dimension(Math.round(t.toHSL().s*100),"%")},lightness:function(t){return new e.Dimension(Math.round(t.toHSL().l*100),"%")},red:function(t){return new e.Dimension(t.rgb[0])},green:function(t){return new e.Dimension(t.rgb[1])},blue:function(t){return new e.Dimension(t.rgb[2])},alpha:function(t){return new e.Dimension(t.toHSL().a)},luma:function(t){return new e.Dimension(Math.round((.2126*(t.rgb[0]/255)+.7152*(t.rgb[1]/255)+.0722*(t.rgb[2]/255))*t.alpha*100),"%")},saturate:function(e,n){var r=e.toHSL();return r.s+=n.value/100,r.s=i(r.s),t(r)},desaturate:function(e,n){var r=e.toHSL();return r.s-=n.value/100,r.s=i(r.s),t(r)},lighten:function(e,n){var r=e.toHSL();return r.l+=n.value/100,r.l=i(r.l),t(r)},darken:function(e,n){var r=e.toHSL();return r.l-=n.value/100,r.l=i(r.l),t(r)},fadein:function(e,n){var r=e.toHSL();return r.a+=n.value/100,r.a=i(r.a),t(r)},fadeout:function(e,n){var r=e.toHSL();return r.a-=n.value/100,r.a=i(r.a),t(r)},fade:function(e,n){var r=e.toHSL();return r.a=n.value/100,r.a=i(r.a),t(r)},spin:function(e,n){var r=e.toHSL(),i=(r.h+n.value)%360;return r.h=i<0?360+i:i,t(r)},mix:function(t,n,r){r||(r=new e.Dimension(50));var i=r.value/100,s=i*2-1,o=t.toHSL().a-n.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[t.rgb[0]*u+n.rgb[0]*a,t.rgb[1]*u+n.rgb[1]*a,t.rgb[2]*u+n.rgb[2]*a],l=t.alpha*i+n.alpha*(1-i);return new e.Color(f,l)},greyscale:function(t){return this.desaturate(t,new e.Dimension(100))},contrast:function(e,t,n,r){return e.rgb?(typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1)),typeof r=="undefined"?r=.43:r=r.value,(.2126*(e.rgb[0]/255)+.7152*(e.rgb[1]/255)+.0722*(e.rgb[2]/255))*e.alpha255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},operate:function(t,n){var r=[];n instanceof e.Color||(n=n.toColor());for(var i=0;i<3;i++)r[i]=e.operate(t,this.rgb[i],n.rgb[i]);return new e.Color(r,this.alpha+n.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype.eval=function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}(n("../tree")),function(e){e.Dimension=function(e,t){this.value=parseFloat(e),this.unit=t||null},e.Dimension.prototype={eval:function(){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(){var e=this.value+this.unit;return e},operate:function(t,n){return new e.Dimension(e.operate(t,this.value,n.value),this.unit||n.unit)},compare:function(t){return t instanceof e.Dimension?t.value>this.value?-1:t.value":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={eval:function(t){return this.value.length>1?new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?this.value[0].eval(t):this},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")}}}(n("../tree")),function(e){e.Import=function(t,n,r,i,s,o){var u=this;this.once=i,this.index=s,this._path=t,this.features=r&&new e.Value(r),this.rootpath=o,t instanceof e.Quoted?this.path=/(\.[a-z]*$)|([\?;].*)$/.test(t.value)?t.value:t.value+".less":this.path=t.value.value||t.value,this.css=/css([\?;].*)?$/.test(this.path),this.css||n.push(this.path,function(t,n,r){t&&(t.index=s),r&&u.once&&(u.skip=r),u.root=n||new e.Ruleset([],[])})},e.Import.prototype={toCSS:function(e){var t=this.features?" "+this.features.toCSS(e):"";return this.css?(typeof this._path.value=="string"&&!/^(?:[a-z-]+:|\/)/.test(this._path.value)&&(this._path.value=this.rootpath+this._path.value),"@import "+this._path.toCSS()+t+";\n"):""},eval:function(t){var n,r=this.features&&this.features.eval(t);return this.skip?[]:this.css?this:(n=new e.Ruleset([],this.root.rules.slice(0)),n.evalImports(t),this.features?new e.Media(n.rules,this.features.value):n.rules)}}}(n("../tree")),function(e){e.JavaScript=function(e,t,n){this.escaped=n,this.expression=e,this.index=t},e.JavaScript.prototype={eval:function(t){var n,r=this,i={},s=this.expression.replace(/@\{([\w-]+)\}/g,function(n,i){return e.jsify((new e.Variable("@"+i,r.index)).eval(t))});try{s=new Function("return ("+s+")")}catch(o){throw{message:"JavaScript evaluation error: `"+s+"`",index:this.index}}for(var u in t.frames[0].variables())i[u.slice(1)]={value:t.frames[0].variables()[u].value,toJS:function(){return this.value.eval(t).toCSS()}};try{n=s.call(i)}catch(o){throw{message:"JavaScript evaluation error: '"+o.name+": "+o.message+"'",index:this.index}}return typeof n=="string"?new e.Quoted('"'+n+'"',n,this.escaped,this.index):Array.isArray(n)?new e.Anonymous(n.join(", ")):new e.Anonymous(n)}}}(n("../tree")),function(e){e.Keyword=function(e){this.value=e},e.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value},compare:function(t){return t instanceof e.Keyword?t.value===this.value?0:1:-1}},e.True=new e.Keyword("true"),e.False=new e.Keyword("false")}(n("../tree")),function(e){e.Media=function(t,n){var r=this.emptySelectors();this.features=new e.Value(n),this.ruleset=new e.Ruleset(r,t),this.ruleset.allowImports=!0},e.Media.prototype={toCSS:function(e,t){var n=this.features.toCSS(t);return this.ruleset.root=e.length===0||e[0].multiMedia,"@media "+n+(t.compress?"{":" {\n ")+this.ruleset.toCSS(e,t).trim().replace(/\n/g,"\n ")+(t.compress?"}":"\n}\n")},eval:function(t){t.mediaBlocks||(t.mediaBlocks=[],t.mediaPath=[]);var n=new e.Media([],[]);return this.debugInfo&&(this.ruleset.debugInfo=this.debugInfo,n.debugInfo=this.debugInfo),n.features=this.features.eval(t),t.mediaPath.push(n),t.mediaBlocks.push(n),t.frames.unshift(this.ruleset),n.ruleset=this.ruleset.eval(t),t.frames.shift(),t.mediaPath.pop(),t.mediaPath.length===0?n.evalTop(t):n.evalNested(t)},variable:function(t){return e.Ruleset.prototype.variable.call(this.ruleset,t)},find:function(){return e.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return e.Ruleset.prototype.rulesets.apply(this.ruleset)},emptySelectors:function(){var t=new e.Element("","&",0);return[new e.Selector([t])]},evalTop:function(t){var n=this;if(t.mediaBlocks.length>1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t,n){var r=[],i=[],s=[],o=[],u=[],a,f,l;this.root||this.joinSelectors(u,t,this.selectors);for(var c=0;c0){f=e.debugInfo(n,this),a=u.map(function(e){return e.map(function(e){return e.toCSS(n)}).join("").trim()}).join(n.compress?",":",\n");for(var c=i.length-1;c>=0;c--)s.indexOf(i[c])===-1&&s.unshift(i[c]);i=s,r.push(f+a+(n.compress?"{":" {\n ")+i.join(n.compress?"":"\n ")+(n.compress?"}":"\n}\n"))}return r.push(o),r.join("")+(n.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0)),v=!1):d=new e.Selector([]),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t)):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e){this.elements=e},e.Selector.prototype.match=function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - options = options || {}; - var importError, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var evaldRoot = evaluate.call(this, evalEnv); - - new(tree.joinSelectorVisitor)() - .run(evaldRoot); - - new(tree.processExtendsVisitor)() - .run(evaldRoot); - - var css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css, options.maxLineLen); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - callback(e); - } - else { - callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")) - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, delim, arg, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new (tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match, extend, extendList = []; - - while ((extend = $(this.extend)) || (e = $(this.element))) { - if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e) - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced, expression = []; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, currentFileInfo, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && currentFileInfo.currentDirectory) { - path = currentFileInfo.currentDirectory + path; - } - var sheetEnv = env.toSheet(path); - sheetEnv.processImports = false; - sheetEnv.currentFileInfo = currentFileInfo; - - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet(sheetEnv, - function (e, root, data, sheet, _, path) { - callback.call(null, e, root, path); - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - return values.value[index]; - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; - } - else { - useBase64 = /;base64$/.test(mimetype) - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } else if (!this.env.silent) { - // if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn - console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - }, - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - type: "Color", - eval: function () { return this }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - // - // 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. - // - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - - if (compress) { - color = color.split(''); - - // Convert color to short format - if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { - color = color[0] + color[2] + color[4]; - } else { - color = color.join(''); - } - } - - 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. - // - operate: function (env, op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - type: "Comment", - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function (env) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && !this.unit.isAngle()) { - return strValue; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - return strValue + this.unit.toCSS(env); - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, conversion, targetUnit, derivedConversions = {}; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(function (atomicUnit, denominator) { - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - - toCSS: function (env) { - if (this.numerator.length >= 1) { - return this.numerator[0]; - } - if (this.denominator.length >= 1) { - return this.denominator[0]; - } - if ((!env || !env.strictUnits) && this.backupUnit) { - return this.backupUnit; - } - return ""; - }, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isAngle: function () { - return tree.UnitConversions.angle.hasOwnProperty(this.toCSS()); - }, - - isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length == 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, groupName, result = {}; - - for (groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(function (atomicUnit) { - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - toCSS: function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - }, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = []; - - for(i = 0; i < selectors.length; i++) { - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - var that = this; - - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined) { - this.css = !this.options.less; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - this.root = visitor.visit(this.root); - }, - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this.path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && !(path instanceof tree.URL)) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - toCSS: function () { return this.value; }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.ruleset = visitor.visit(this.ruleset); - }, - toCSS: function (env) { - var features = this.features.toCSS(env); - - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - toCSS: function () { return ""; }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval(new(tree.evalEnv)(env, - [this, frame].concat(mixinFrames))); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] - .concat(env.frames)))) { - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '-' + this.value.toCSS(env); - }, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { type: "Operation", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - toCSS: function (env) { - var separator = this.isSpaced ? " " : ""; - return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS(); - } -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '(' + this.value.toCSS(env).trim() + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.variable) { return "" } - else { - try { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - } - }, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && env.strictMath === false) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.index, this.currentFileInfo, this.inline); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - this.selectors = visitor.visit(this.selectors); - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - if (this.firstRoot && rule instanceof tree.Rule) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null}; - } - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - // Remove last semicolon - if (env.compress && rules.length) { - rule = rules[rules.length - 1]; - if (rule.charAt(rule.length - 1) === ';') { - rules[rules.length - 1] = rule.substring(0, rule.length - 1); - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = this.paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0), selector.extendList); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([], selector.extendList); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel, extendList; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements, extendList) { - this.elements = elements; - this.extendList = extendList || []; -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList) - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; - }, - eval: function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - })); - }, - toCSS: function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'strictImports', // option - - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'mime', // browser only - mime type for sheet import - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' = absolute path to the entry file - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = options.filename || "input"; - options.filename = null; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: options.rootpath || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - tree.parseEnv.prototype.toSheet = function (path) { - var env = new tree.parseEnv(this); - env.href = path; - //env.title = path; - env.type = this.mime; - return env; - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits' // whether units need to evaluate correctly - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - } -})(require('./tree'));(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - } - }; - -})(require('./tree'));(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode; - - if (!importNode.css) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && !evaldImportNode.css) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - if (imported && !importNode.options.multiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - } else { - subFinish(); - } - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree'));(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.ruleset.root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree'));(function (tree) { - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - } - } - - // now find every selector and apply the extends that apply to all extends - // and the ones which apply to an individual extend - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator == '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - return false; - }, - extendSelector:function (matches, selectorPath, replacementSelector) { - - //for a set of matches, replace each match with the replacement selector - - var currentSelectorPathIndex = 0, - currentSelectorPathElementIndex = 0, - path = [], - matchIndex, - selector, - firstElement; - - for (matchIndex = 0; matchIndex < matches.length; matchIndex++) { - match = matches[matchIndex]; - selector = selectorPath[match.pathIndex]; - firstElement = new tree.Element( - match.initialCombinator, - replacementSelector.elements[0].value, - replacementSelector.elements[0].index - ); - - if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)) - )); - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selector.elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree'));// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -var cache = null; - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -var session_cache = ''; -less.modifyVars = function(record) { - var str = session_cache; - for (name in record) { - str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - new(less.Parser)(new less.tree.parseEnv(less)).parse(str, function (e, root) { - if (e) { - error(e, "session_cache"); - } else { - createCSS(root.toCSS(less), less.sheets[less.sheets.length - 1]); - } - }); -}; - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - var env = new less.tree.parseEnv(less); - env.filename = document.location.href.replace(/#.*$/, ''); - - new(less.Parser)(env).parse(styles[i].innerHTML || '', function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace("\\", "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(sheet.href, window.location.href); - var href = hrefParts.url; - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - var env; - var newFileInfo = { - relativeUrls: less.relativeUrls, - currentDirectory: hrefParts.path, - filename: href - }; - - if (sheet instanceof less.tree.parseEnv) { - env = new less.tree.parseEnv(sheet); - newFileInfo.entryPath = env.currentFileInfo.entryPath; - newFileInfo.rootpath = env.currentFileInfo.rootpath; - newFileInfo.rootFilename = env.currentFileInfo.rootFilename; - } else { - env = new less.tree.parseEnv(less); - env.mime = sheet.type; - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - } - - if (env.relativeUrls) { - //todo - this relies on option being set on less object rather than being passed in as an option - // - need an originalRootpath - if (less.rootpath) { - newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - xhr(href, sheet.type, function (data, lastModified) { - // Store data this session - session_cache += data.replace(/@import .+?;/ig, ''); - - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }, href); - } else { - // Use remote copy (re-parse) - try { - env.contents[href] = data; // Updating content cache - env.paths = [hrefParts.path]; - env.currentFileInfo = newFileInfo; - - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href); - //TODO - there must be a better way? A generic less-to-css function that can both call error - //and removeNode where appropriate - //should also add tests - if (env.currentFileInfo.rootFilename === href) { - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } - } catch (e) { - callback(e, null, null, sheet); - } - }); - } catch (e) { - callback(e, null, null, sheet); - } - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, null, sheet); - }); -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss == null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); - } - if (oldCss && keepOldCss === false) { - head.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] != undefined) { - error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} -})(window); diff --git a/dist/less-1.4.0-beta.min.js b/dist/less-1.4.0-beta.min.js deleted file mode 100644 index 4f0ee4f3a..000000000 --- a/dist/less-1.4.0-beta.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * LESS - Leaner CSS v1.4.0 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache 2.0 License. - * - * @licence - */(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,i,s){e?k(e,i.href):t&&S(t.toCSS(r),i,s.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=b(t.href,e.location.href),u=o.url,a=l&&l.getItem(u),f=l&&l.getItem(u+":timestamp"),c={css:a,timestamp:f},h,p={relativeUrls:r.relativeUrls,currentDirectory:o.path,filename:u};t instanceof r.tree.parseEnv?(h=new r.tree.parseEnv(t),p.entryPath=h.currentFileInfo.entryPath,p.rootpath=h.currentFileInfo.rootpath,p.rootFilename=h.currentFileInfo.rootFilename):(h=new r.tree.parseEnv(r),h.mime=t.type,p.entryPath=o.path,p.rootpath=r.rootpath||o.path,p.rootFilename=u),h.relativeUrls&&(r.rootpath?p.rootpath=b(r.rootpath+y(o.path,p.entryPath)).path:p.rootpath=o.path),x(u,t.type,function(e,a){v+=e.replace(/@import .+?;/ig,"");if(!i&&c&&a&&(new Date(a)).valueOf()===(new Date(c.timestamp)).valueOf())S(c.css,t),n(null,null,e,t,{local:!0,remaining:s},u);else try{h.contents[u]=e,h.paths=[o.path],h.currentFileInfo=p,(new r.Parser(h)).parse(e,function(r,i){if(r)return n(r,null,null,t);try{n(r,i,e,t,{local:!1,lastModified:a,remaining:s},u),h.currentFileInfo.rootFilename===u&&N(document.getElementById("less-error-message:"+E(u)))}catch(r){n(r,null,null,t)}})}catch(f){n(f,null,null,t)}},function(e,r){n({type:"File",message:"'"+r+"' wasn't found ("+e+")"},null,null,t)})}function E(e){return e.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r=t.href||"",i="less:"+(t.title||E(r)),s=document.getElementById(i),o=!1,u=document.createElement("style");u.setAttribute("type","text/css"),t.media&&u.setAttribute("media",t.media),u.id=i;if(u.styleSheet)try{u.styleSheet.cssText=e}catch(a){throw new Error("Couldn't reassign styleSheet.cssText.")}else u.appendChild(document.createTextNode(e)),o=s!==null&&s.childNodes.length>0&&u.childNodes.length>0&&s.firstChild.nodeValue===u.firstChild.nodeValue;var f=document.getElementsByTagName("head")[0];if(s==null||o===!1){var c=t&&t.nextSibling||null;(c||document.getElementsByTagName("head")[0]).parentNode.insertBefore(u,c)}s&&o===!1&&f.removeChild(s);if(n&&l){C("saving "+r+" to cache.");try{l.setItem(r,e),l.setItem(r+":timestamp",n)}catch(a){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,n){var i="less-error-message:"+E(n||""),s='
  • {content}
  • ',o=document.createElement("div"),u,a,f=[],l=e.filename||n,c=l.match(/([^\/]+(\?.*)?)$/)[1];o.id=i,o.className="less-error-message",a="

    "+(e.type||"Syntax")+"Error: "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+c+" ";var h=function(e,n,r){e.extract[n]!=t&&f.push(s.replace(/\{line\}/,(parseInt(e.line)||0)+(n-1)).replace(/\{class\}/,r).replace(/\{content\}/,e.extract[n]))};e.extract?(h(e,0,""),h(e,1,"line"),h(e,2,""),a+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+f.join("")+"
    "):e.stack&&(a+="
    "+e.stack.split("\n").slice(1).join("
    ")),o.innerHTML=a,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),o.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(u=setInterval(function(){document.body&&(document.getElementById(i)?document.body.replaceChild(o,document.getElementById(i)):document.body.insertBefore(o,document.body.firstChild),clearInterval(u))},10))}var r,i,s;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof e=="undefined"?r={}:r=e.less={},i=r.tree={},r.mode="rhino"):typeof e=="undefined"?(r=exports,i=n("./tree"),r.mode="node"):(typeof e.less=="undefined"&&(e.less={}),r=e.less,i=e.less.tree={},r.mode="browser"),r.Parser=function(t){function m(){a=c[u],f=o,h=o}function g(){c[u]=a,o=f,h=o}function y(){o>h&&(c[u]=c[u].slice(o-h),h=o)}function b(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function w(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,y();else{y();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return E(r),typeof t=="string"?t:t.length===1?t[0]:t}function E(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function k(e,t,i){var s=i.currentFileInfo.filename;return r.mode!=="browser"&&r.mode!=="rhino"&&(s=n("path").resolve(s)),{lineNumber:C(e,t).line+1,fileName:s}}function L(e,t){var n=N(e,t),r=C(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.currentFileInfo.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&C(e.call,n).line+1,this.callExtract=o[C(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this;t instanceof i.parseEnv||(t=new i.parseEnv(t));var v=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n,i){var s=this;this.queue.push(e),r.Parser.importer(e,n,function(t,n,r){s.queue.splice(s.queue.indexOf(e),1);var o=r in s.files;s.files[r]=n,t&&!s.error&&(s.error=t),i(t,n,o)},t)}};return L.prototype=new Error,L.prototype.constructor=L,this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,p={imports:v,parse:function(e,a){var f,d,v,m,g,y,b=[],E,S=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.currentFileInfo.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(S)return a(new L(S,t));try{f=new i.Ruleset([],w(this.parsers.primary)),f.root=!0,f.firstRoot=!0}catch(x){return a(new L(x,t))}f.toCSS=function(e){var s,o,u;return function(s,o){s=s||{};var u,a=new i.evalEnv(s);typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),a.frames=[new i.Ruleset(null,o)]);try{var f=e.call(this,a);(new i.joinSelectorVisitor).run(f),(new i.processExtendsVisitor).run(f);var l=f.toCSS({compress:Boolean(s.compress),dumpLineNumbers:t.dumpLineNumbers,strictUnits:Boolean(s.strictUnits)})}catch(c){throw new L(c,t)}return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(l,s.maxLineLen):s.compress?l.replace(/(\s)+/g,"$1"):l}}(f.eval);if(o=0&&s.charAt(T)!=="\n";T--)N++;S={type:"Parse",message:"Unrecognised input",index:o,filename:t.currentFileInfo.filename,line:g,column:N,extract:[y[g-2],y[g-1],y[g]]}}var C=function(e){e=S||e||p.imports.error,e?(e instanceof L||(e=new L(e,t)),a(e)):a(null,f)};t.processImports!==!1?(new i.importVisitor(this.imports,C)).run(f):C()},parsers:{primary:function(){var e,t=[];while((e=w(this.extendRule)||w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/)||w(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(w(/^\/\/.*/),!0);if(e=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,n=o,r,u=o;s.charAt(n)==="~"&&(n++,r=!0);if(s.charAt(n)!=='"'&&s.charAt(n)!=="'")return;r&&w("~");if(e=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],r,u,t.currentFileInfo)},keyword:function(){var e;if(e=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=w(this.alpha);if(typeof s!="undefined")return s}w("("),r=w(this.entities.arguments);if(!w(")"))return;if(e)return new i.Call(e,r,a,t.currentFileInfo)},arguments:function(){var e=[],t;while(t=w(this.entities.assignment)||w(this.expression)){e.push(t);if(!w(","))break}return e},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)||w(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=w(/^\w+(?=\s?=)/i))&&w("=")&&(t=w(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!w(/^url\(/))return;return e=w(this.entities.quoted)||w(this.entities.variable)||w(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",S(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.currentFileInfo)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=w(/^@@?[\w-]+/)))return new i.Variable(e,n,t.currentFileInfo)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=w(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.currentFileInfo)},color:function(){var e;if(s.charAt(o)==="#"&&(e=w(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=w(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))return new i.Dimension(e[1],e[2])},unicodeDescriptor:function(){var e;if(e=w(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&w("~");if(e=w(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=w(/^(@[\w-]+)\s*:/)))return e[1]},extend:function(e){var t,n,r=o,s,u=[];if(!w(e?/^&:extend\(/:/^:extend\(/))return;do{s=null,t=[];for(;;){s=w(/^(all)(?=\s*(\)|,))/);if(s)break;n=w(this.element);if(!n)break;t.push(n)}s=s&&s[1],u.push(new i.Extend(new i.Selector(t),s,r))}while(w(","));return S(/^\)/),e&&S(/^;/),u},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var e=[],n,r,u,a,f,l=o,c=s.charAt(o),h=!1;if(c!=="."&&c!=="#")return;m();while(n=w(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=w(">");w("(")&&(u=this.mixin.args.call(this,!0).args,S(")")),u=u||[],w(this.important)&&(h=!0);if(e.length>0&&(w(";")||T("}")))return new i.mixin.Call(e,u,l,t.currentFileInfo,h);g()},args:function(e){var t=[],n=[],r,u=[],a,f,l,c,h,p={args:null,variadic:!1};for(;;){if(e)h=w(this.expression);else{w(this.comment);if(s.charAt(o)==="."&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({variadic:!0});break}h=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)}if(!h)break;l=null,h.throwAwayComments&&h.throwAwayComments(),c=h;var d=null;if(e){if(h.value.length==1)var d=h.value[0]}else d=h;if(d&&d instanceof i.Variable)if(w(":"))t.length>0&&(r&&x("Cannot mix ; and , as delimiter types"),a=!0),c=S(this.expression),l=f=d.name;else{if(!e&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({name:h.name,variadic:!0});break}e||(f=l=d.name,c=null)}c&&t.push(c),u.push({name:l,value:c});if(w(","))continue;if(w(";")||r)a&&x("Cannot mix ; and , as delimiter types"),r=!0,t.length>1&&(c=new i.Value(t)),n.push({name:f,value:c}),f=null,t=[],a=!1}return p.args=r?n:u,p},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||T(/^[^{]*\}/))return;m();if(n=w(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];var h=this.mixin.args.call(this,!1);t=h.args,c=h.variadic,w(")")||(l=o,g()),w(this.comment),w(/^when/)&&(f=S(this.conditions,"expected condition")),r=w(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);g()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||T("}")},alpha:function(){var e;if(!w(/^\(opacity=/i))return;if(e=w(/^\d+/)||w(this.entities.variable))return S(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=w(this.combinator),e=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||w("*")||w("&")||w(this.attribute)||w(/^\([^()@]+\)/)||w(/^[\.#](?=@)/)||w(this.entities.variableCurly),e||w("(")&&(r=w(this.selector))&&w(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e,t=s.charAt(o);if(t===">"||t==="+"||t==="~"||t==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(t)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u,a,f=[];while((a=w(this.extend))||(t=w(this.element))){a?f.push.apply(f,a):(f.length&&x("Extend can only be used at the end of selector"),r=s.charAt(o),n.push(t),t=null);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n,f);f.length&&x("Extend must be used to extend a selector, it cannot be used on its own")},attribute:function(){var e="",t,n,r;if(!w("["))return;(t=w(this.entities.variableCurly))||(t=S(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/));if(r=w(/^[|~*$^]?=/))n=w(this.entities.quoted)||w(/^[\w-]+/)||w(this.entities.variableCurly);return S("]"),new i.Attribute(t,r,n)},block:function(){var e;if(w("{")&&(e=w(this.primary))&&w("}"))return e},ruleset:function(){var e=[],n,r,u,a;m(),t.dumpLineNumbers&&(a=k(o,s,t));while(n=w(this.selector)){e.push(n),w(this.comment);if(!w(","))break;w(this.comment)}if(e.length>0&&(r=w(this.block))){var f=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(f.debugInfo=a),f}l=o,g()},rule:function(e){var n,r,u=s.charAt(o),a,c;m();if(u==="."||u==="#"||u==="&")return;if(n=w(this.variable)||w(this.property)){r=!e&&(t.compress||n.charAt(0)==="@")?w(this.value)||w(this.anonymousValue):w(this.anonymousValue)||w(this.value),a=w(this.important);if(r&&w(this.end))return new i.Rule(n,r,a,f,t.currentFileInfo);l=o,g();if(r&&!e)return this.rule(!0)}},anonymousValue:function(){if(match=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))return o+=match[0].length-1,new i.Anonymous(match[1])},"import":function(){var e,n,r=o;m();var s=w(/^@import?\s+/),u=(s?w(this.importOptions):null)||{};if(s&&(e=w(this.entities.quoted)||w(this.entities.url))){n=w(this.mediaFeatures);if(w(";"))return n=n&&new i.Value(n),new i.Import(e,n,u,r,t.currentFileInfo)}g()},importOptions:function(){var e,t={},n,r;if(!w("("))return null;do if(e=w(this.importOption)){n=e,r=!0;switch(n){case"css":n="less",r=!1;break;case"once":n="multiple",r=!1}t[n]=r;if(!w(","))break}while(e);return S(")"),t},importOption:function(){var e=w(/^(less|css|multiple|once)/);if(e)return e[1]},mediaFeature:function(){var e,n,r=[];do if(e=w(this.entities.keyword))r.push(e);else if(w("(")){n=w(this.property),e=w(this.value);if(!w(")"))return null;if(n&&e)r.push(new i.Paren(new i.Rule(n,e,null,o,t.currentFileInfo,!0)));else{if(!e)return null;r.push(new i.Paren(e))}}while(e);if(r.length>0)return new i.Expression(r)},mediaFeatures:function(){var e,t=[];do if(e=w(this.mediaFeature)){t.push(e);if(!w(","))break}else if(e=w(this.entities.variable)){t.push(e);if(!w(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=k(o,s,t));if(w(/^@media/)){e=w(this.mediaFeatures);if(n=w(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=w(this["import"])||w(this.media))return n;m(),e=w(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;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":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(w(/^[^{]+/)||"").trim());if(c){if(r=w(this.block))return new i.Directive(e,r)}else if((n=p?w(this.expression):w(this.entity))&&w(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=k(o,s,t)),d}g()},value:function(){var e,t=[],n;while(e=w(this.expression)){t.push(e);if(!w(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return w(/^! *important/)},sub:function(){var e,t;if(w("("))if(e=w(this.addition))return t=new i.Expression([e]),S(")"),t.parens=!0,t},multiplication:function(){var e,t,n,r,u,a=[];if(e=w(this.operand)){u=b(s.charAt(o-1));while(!T(/^\/[*\/]/)&&(n=w("/")||w("*"))){if(!(t=w(this.operand)))break;e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1))}return r||e}},addition:function(){var e,t,n,r,u;if(e=w(this.multiplication)){u=b(s.charAt(o-1));while((n=w(/^[-+]\s+/)||!u&&(w("+")||w("-")))&&(t=w(this.multiplication)))e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1));return r||e}},conditions:function(){var e,t,n=o,r;if(e=w(this.condition)){while(w(",")&&(t=w(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;w(/^not/)&&(u=!0),S("(");if(e=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(r=w(/^(?:>=|=<|[<=>])/))?(t=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):x("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),S(")"),w(/^and/)?new i.Condition("and",n,w(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=w("-"));var n=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return e&&(n.parensInOp=!0,n=new i.Negative(n)),n},expression:function(){var e,t,n=[],r;while(e=w(this.addition)||w(this.entity))n.push(e),!T(/^\/[\/*]/)&&(t=w("/"))&&n.push(new i.Anonymous(t));if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=w(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.currentDirectory&&(e=t.currentDirectory+e);var i=r.toSheet(e);i.processImports=!1,i.currentFileInfo=t,w(i,function(e,t,r,i,s,o){n.call(null,e,t,o)},!0)};(function(r){function u(e){return r.functions.hsla(e.h,e.s,e.l,e.a)}function a(e,t){return e instanceof r.Dimension&&e.unit.is("%")?parseFloat(e.value*t/100):f(e)}function f(e){if(e instanceof r.Dimension)return parseFloat(e.unit.is("%")?e.value/100:e.value);if(typeof e=="number")return e;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function l(e){return Math.min(1,Math.max(0,e))}r.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(e,t,n,i){var s=[e,t,n].map(function(e){return a(e,256)});return i=f(i),new r.Color(s,i)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,r){function o(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?s+(i-s)*e*6:e*2<1?i:e*3<2?s+(i-s)*(2/3-e)*6:s}e=f(e)%360/360,t=l(f(t)),n=l(f(n)),r=l(f(r));var i=n<=.5?n*(t+1):n+t-n*t,s=n*2-i;return this.rgba(o(e+1/3)*255,o(e)*255,o(e-1/3)*255,r)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,r){e=f(e)%360/360*360,t=f(t),n=f(n),r=f(r);var i,s;i=Math.floor(e/60%6),s=e/60-i;var o=[n,n*(1-t),n*(1-s*t),n*(1-(1-s)*t)],u=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(o[u[i][0]]*255,o[u[i][1]]*255,o[u[i][2]]*255,r)},hue:function(e){return new r.Dimension(Math.round(e.toHSL().h))},saturation:function(e){return new r.Dimension(Math.round(e.toHSL().s*100),"%")},lightness:function(e){return new r.Dimension(Math.round(e.toHSL().l*100),"%")},hsvhue:function(e){return new r.Dimension(Math.round(e.toHSV().h))},hsvsaturation:function(e){return new r.Dimension(Math.round(e.toHSV().s*100),"%")},hsvvalue:function(e){return new r.Dimension(Math.round(e.toHSV().v*100),"%")},red:function(e){return new r.Dimension(e.rgb[0])},green:function(e){return new r.Dimension(e.rgb[1])},blue:function(e){return new r.Dimension(e.rgb[2])},alpha:function(e){return new r.Dimension(e.toHSL().a)},luma:function(e){return new r.Dimension(Math.round(e.luma()*e.alpha*100),"%")},saturate:function(e,t){var n=e.toHSL();return n.s+=t.value/100,n.s=l(n.s),u(n)},desaturate:function(e,t){var n=e.toHSL();return n.s-=t.value/100,n.s=l(n.s),u(n)},lighten:function(e,t){var n=e.toHSL();return n.l+=t.value/100,n.l=l(n.l),u(n)},darken:function(e,t){var n=e.toHSL();return n.l-=t.value/100,n.l=l(n.l),u(n)},fadein:function(e,t){var n=e.toHSL();return n.a+=t.value/100,n.a=l(n.a),u(n)},fadeout:function(e,t){var n=e.toHSL();return n.a-=t.value/100,n.a=l(n.a),u(n)},fade:function(e,t){var n=e.toHSL();return n.a=t.value/100,n.a=l(n.a),u(n)},spin:function(e,t){var n=e.toHSL(),r=(n.h+t.value)%360;return n.h=r<0?360+r:r,u(n)},mix:function(e,t,n){n||(n=new r.Dimension(50));var i=n.value/100,s=i*2-1,o=e.toHSL().a-t.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[e.rgb[0]*u+t.rgb[0]*a,e.rgb[1]*u+t.rgb[1]*a,e.rgb[2]*u+t.rgb[2]*a],l=e.alpha*i+t.alpha*(1-i);return new r.Color(f,l)},greyscale:function(e){return this.desaturate(e,new r.Dimension(100))},contrast:function(e,t,n,r){if(!e.rgb)return null;typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1));if(t.luma()>n.luma()){var i=n;n=t,t=i}return typeof r=="undefined"?r=.43:r=f(r),e.luma()*e.alpha=d){if(this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",o,v,d),(new r.URL(i||t,this.currentFileInfo)).eval(this.env);this.env.silent||console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!",o,v,d)}p=f?p.toString("base64"):encodeURIComponent(p);var m="'data:"+s+","+p+"'";return new r.URL(new r.Anonymous(m))}},r._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(e){var i=n("path").extname(e),s=r._mime._types[i];if(s===t)throw new Error('Optional dependency "mime" is required for '+i);return s},charsets:{lookup:function(e){return e&&/^text\//.test(e)?"UTF-8":""}}};var i=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],s=function(e,t){return function(n){return t!=null&&(n=n.unify()),this._math(Math[e],t,n)}};for(var o=0;o255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("");return n&&(r=r.split(""),r[0]==r[1]&&r[2]==r[3]&&r[4]==r[5]?r=r[0]+r[2]+r[4]:r=r.join("")),"#"+r},operate:function(t,n,r){var i=[];r instanceof e.Color||(r=r.toColor());for(var s=0;s<3;s++)i[s]=e.operate(t,n,this.rgb[s],r.rgb[s]);return new e.Color(i,this.alpha+r.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={type:"Comment",toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype={type:"Condition",accept:function(e){this.lvalue=e.visit(this.lvalue),this.rvalue=e.visit(this.rvalue)},eval:function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}}(n("../tree")),function(e){e.Dimension=function(n,r){this.value=parseFloat(n),this.unit=r&&r instanceof e.Unit?r:new e.Unit(r?[r]:t)},e.Dimension.prototype={type:"Dimension",accept:function(e){this.unit=e.visit(this.unit)},eval:function(e){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(e){if(e&&e.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var t=this.value,n=String(t);t!==0&&t<1e-6&&t>-0.000001&&(n=t.toFixed(20).replace(/0+$/,""));if(e&&e.compress){if(t===0&&!this.unit.isAngle())return n;t>0&&t<1&&(n=n.substr(1))}return n+this.unit.toCSS(e)},operate:function(t,n,r){var i=e.operate(t,n,this.value,r.value),s=this.unit.clone();if(n==="+"||n==="-"){if(s.numerator.length===0&&s.denominator.length===0)s.numerator=r.unit.numerator.slice(0),s.denominator=r.unit.denominator.slice(0);else if(r.unit.numerator.length!=0||s.denominator.length!=0){r=r.convertTo(this.unit.usedUnits());if(t.strictUnits&&r.unit.toString()!==s.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+s.toString()+"' and '"+r.unit.toString()+"'.");i=e.operate(t,n,this.value,r.value)}}else n==="*"?(s.numerator=s.numerator.concat(r.unit.numerator).sort(),s.denominator=s.denominator.concat(r.unit.denominator).sort(),s.cancel()):n==="/"&&(s.numerator=s.numerator.concat(r.unit.denominator).sort(),s.denominator=s.denominator.concat(r.unit.numerator).sort(),s.cancel());return new e.Dimension(i,s)},compare:function(t){if(t instanceof e.Dimension){var n=this.unify(),r=t.unify(),i=n.value,s=r.value;return s>i?-1:s=1?this.numerator[0]:this.denominator.length>=1?this.denominator[0]:(!e||!e.strictUnits)&&this.backupUnit?this.backupUnit:""},toString:function(){var e,t=this.numerator.join("*");for(e=0;e0)for(n=0;n":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={type:"Expression",accept:function(e){this.value=e.visit(this.value)},eval:function(t){var n,r=this.parens&&!this.parensInOp,i=!1;return r&&t.inParenthesis(),this.value.length>1?n=new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?(this.value[0].parens&&!this.value[0].parensInOp&&(i=!0),n=this.value[0].eval(t)):n=this,r&&t.outOfParenthesis(),this.parens&&this.parensInOp&&!t.isMathOn()&&!i&&(n=new e.Paren(n)),n},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")},throwAwayComments:function(){this.value=this.value.filter(function(t){return!(t instanceof e.Comment)})}}}(n("../tree")),function(e){e.Extend=function(t,n,r){this.selector=t,this.option=n,this.index=r;switch(n){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}},e.Extend.prototype={type:"Extend",accept:function(e){this.selector=e.visit(this.selector)},eval:function(t){return new e.Extend(this.selector.eval(t),this.option,this.index)},clone:function(t){return new e.Extend(this.selector,this.option,this.index)},findSelfSelectors:function(e){var t=[];for(d=0;d1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t){var n=[],r=[],i=[],s=[],o,u,a;for(var f=0;f0){u=e.debugInfo(t,this),o=this.paths.map(function(e){return e.map(function(e){return e.toCSS(t)}).join("").trim()}).join(t.compress?",":",\n");for(var f=r.length-1;f>=0;f--)(r[f].slice(0,2)==="/*"||i.indexOf(r[f])===-1)&&i.unshift(r[f]);r=i,n.push(u+o+(t.compress?"{":" {\n ")+r.join(t.compress?"":"\n ")+(t.compress?"}":"\n}\n"))}return n.push(s),n.join("")+(t.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0),r.extendList),v=!1):d=new e.Selector([],r.extendList),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0&&t.push(a[i])},mergeElementsOnToSelectors:function(t,n){var r,i,s;if(n.length==0){n.push([new e.Selector(t)]);return}for(r=0;r0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t),i[i.length-1].extendList):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e,t){this.elements=e,this.extendList=t||[]},e.Selector.prototype={type:"Selector",accept:function(e){this.elements=e.visit(this.elements),this.extendList=e.visit(this.extendList)},match:function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree")),function(e){var t=["paths","optimization","files","contents","relativeUrls","strictImports","dumpLineNumbers","compress","processImports","mime","currentFileInfo"];e.parseEnv=function(e){r(e,this,t),this.contents||(this.contents={}),this.files||(this.files={});if(!this.currentFileInfo){var n=e.filename||"input";e.filename=null;var i=n.replace(/[^\/\\]*$/,"");this.currentFileInfo={filename:n,relativeUrls:this.relativeUrls,rootpath:e.rootpath||"",currentDirectory:i,entryPath:i,rootFilename:n}}},e.parseEnv.prototype.toSheet=function(t){var n=new e.parseEnv(this);return n.href=t,n.type=this.mime,n};var n=["silent","verbose","compress","ieCompat","strictMath","strictUnits"];e.evalEnv=function(e,t){r(e,this,n),this.frames=t||[]},e.evalEnv.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},e.evalEnv.prototype.outOfParenthesis=function(){this.parensStack.pop()},e.evalEnv.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},e.evalEnv.prototype.isPathRelative=function(e){return!/^(?:[a-z-]+:|\/)/.test(e)};var r=function(e,t,n){if(!e)return;for(var r=0;r100){var p="{unable to calculate}",d="{unable to calculate}";try{p=u[0].selfSelectors[0].toCSS(),d=u[0].selector.toCSS()}catch(v){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+p+":extend("+d+")"}}return u.concat(f.doExtendChaining(u,n,r+1))}return u},inInheritanceChain:function(e,t){if(e===t)return!0;if(t.parents){if(this.inInheritanceChain(e,t.parents[0]))return!0;if(this.inInheritanceChain(e,t.parents[1]))return!0}return!1},visitRule:function(e,t){t.visitDeeper=!1},visitMixinDefinition:function(e,t){t.visitDeeper=!1},visitSelector:function(e,t){t.visitDeeper=!1},visitRuleset:function(e,t){if(e.root)return;var n,r,i,s=this.allExtendsStack[this.allExtendsStack.length-1],o=[],u=this;for(i=0;i0&&f[c.matched].combinator.value!==o?c=null:c.matched++,c&&(c.finished=c.matched===f.length,c.finished&&!e.allowAfter&&(i+1i&&s>0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,match.pathIndex)),o.push(new e.Selector(a.elements.slice(s,match.index).concat([f]).concat(r.elements.slice(1)))),i=match.endPathIndex,s=match.endPathElementIndex,s>=a.elements.length&&(s=0,i++);return i0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,n.length)),o},visitRulesetOut:function(e){},visitMedia:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitMediaOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitDirectiveOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - options = options || {}; - var importError, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var evaldRoot = evaluate.call(this, evalEnv); - - new(tree.joinSelectorVisitor)() - .run(evaldRoot); - - new(tree.processExtendsVisitor)() - .run(evaldRoot); - - var css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css, options.maxLineLen); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - callback(e); - } - else { - callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")) - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, delim, arg, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new (tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match, extend, extendList = []; - - while ((extend = $(this.extend)) || (e = $(this.element))) { - if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e) - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced, expression = []; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, currentFileInfo, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && currentFileInfo.currentDirectory) { - path = currentFileInfo.currentDirectory + path; - } - var sheetEnv = env.toSheet(path); - sheetEnv.processImports = false; - sheetEnv.currentFileInfo = currentFileInfo; - - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet(sheetEnv, - function (e, root, data, sheet, _, path) { - callback.call(null, e, root, path); - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - return values.value[index]; - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; - } - else { - useBase64 = /;base64$/.test(mimetype) - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } else if (!this.env.silent) { - // if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn - console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - }, - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - type: "Color", - eval: function () { return this }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - // - // 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. - // - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - - if (compress) { - color = color.split(''); - - // Convert color to short format - if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { - color = color[0] + color[2] + color[4]; - } else { - color = color.join(''); - } - } - - 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. - // - operate: function (env, op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - type: "Comment", - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function (env) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && !this.unit.isAngle()) { - return strValue; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - return strValue + this.unit.toCSS(env); - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, conversion, targetUnit, derivedConversions = {}; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(function (atomicUnit, denominator) { - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - - toCSS: function (env) { - if (this.numerator.length >= 1) { - return this.numerator[0]; - } - if (this.denominator.length >= 1) { - return this.denominator[0]; - } - if ((!env || !env.strictUnits) && this.backupUnit) { - return this.backupUnit; - } - return ""; - }, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isAngle: function () { - return tree.UnitConversions.angle.hasOwnProperty(this.toCSS()); - }, - - isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length == 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, groupName, result = {}; - - for (groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(function (atomicUnit) { - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - toCSS: function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - }, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = []; - - for(i = 0; i < selectors.length; i++) { - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - var that = this; - - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined) { - this.css = !this.options.less; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - this.root = visitor.visit(this.root); - }, - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this.path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && !(path instanceof tree.URL)) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - toCSS: function () { return this.value; }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.ruleset = visitor.visit(this.ruleset); - }, - toCSS: function (env) { - var features = this.features.toCSS(env); - - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - toCSS: function () { return ""; }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval(new(tree.evalEnv)(env, - [this, frame].concat(mixinFrames))); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] - .concat(env.frames)))) { - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '-' + this.value.toCSS(env); - }, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { type: "Operation", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - toCSS: function (env) { - var separator = this.isSpaced ? " " : ""; - return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS(); - } -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '(' + this.value.toCSS(env).trim() + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.variable) { return "" } - else { - try { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - } - }, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && env.strictMath === false) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.index, this.currentFileInfo, this.inline); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - this.selectors = visitor.visit(this.selectors); - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - if (this.firstRoot && rule instanceof tree.Rule) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null}; - } - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - // Remove last semicolon - if (env.compress && rules.length) { - rule = rules[rules.length - 1]; - if (rule.charAt(rule.length - 1) === ';') { - rules[rules.length - 1] = rule.substring(0, rule.length - 1); - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = this.paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0), selector.extendList); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([], selector.extendList); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel, extendList; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements, extendList) { - this.elements = elements; - this.extendList = extendList || []; -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList) - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; - }, - eval: function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - })); - }, - toCSS: function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'strictImports', // option - - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'mime', // browser only - mime type for sheet import - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' = absolute path to the entry file - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = options.filename || "input"; - options.filename = null; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: options.rootpath || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - tree.parseEnv.prototype.toSheet = function (path) { - var env = new tree.parseEnv(this); - env.href = path; - //env.title = path; - env.type = this.mime; - return env; - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits' // whether units need to evaluate correctly - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - } -})(require('./tree'));(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - } - }; - -})(require('./tree'));(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode; - - if (!importNode.css) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && !evaldImportNode.css) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - if (imported && !importNode.options.multiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - } else { - subFinish(); - } - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree'));(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.ruleset.root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree'));(function (tree) { - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - } - } - - // now find every selector and apply the extends that apply to all extends - // and the ones which apply to an individual extend - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator == '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - return false; - }, - extendSelector:function (matches, selectorPath, replacementSelector) { - - //for a set of matches, replace each match with the replacement selector - - var currentSelectorPathIndex = 0, - currentSelectorPathElementIndex = 0, - path = [], - matchIndex, - selector, - firstElement; - - for (matchIndex = 0; matchIndex < matches.length; matchIndex++) { - match = matches[matchIndex]; - selector = selectorPath[match.pathIndex]; - firstElement = new tree.Element( - match.initialCombinator, - replacementSelector.elements[0].value, - replacementSelector.elements[0].index - ); - - if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)) - )); - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selector.elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree'));// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -var cache = null; - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -var session_cache = ''; -less.modifyVars = function(record) { - var str = session_cache; - for (name in record) { - str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - new(less.Parser)(new less.tree.parseEnv(less)).parse(str, function (e, root) { - if (e) { - error(e, "session_cache"); - } else { - createCSS(root.toCSS(less), less.sheets[less.sheets.length - 1]); - } - }); -}; - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - var env = new less.tree.parseEnv(less); - env.filename = document.location.href.replace(/#.*$/, ''); - - new(less.Parser)(env).parse(styles[i].innerHTML || '', function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace("\\", "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(sheet.href, window.location.href); - var href = hrefParts.url; - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - var env; - var newFileInfo = { - relativeUrls: less.relativeUrls, - currentDirectory: hrefParts.path, - filename: href - }; - - if (sheet instanceof less.tree.parseEnv) { - env = new less.tree.parseEnv(sheet); - newFileInfo.entryPath = env.currentFileInfo.entryPath; - newFileInfo.rootpath = env.currentFileInfo.rootpath; - newFileInfo.rootFilename = env.currentFileInfo.rootFilename; - } else { - env = new less.tree.parseEnv(less); - env.mime = sheet.type; - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - } - - if (env.relativeUrls) { - //todo - this relies on option being set on less object rather than being passed in as an option - // - need an originalRootpath - if (less.rootpath) { - newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - xhr(href, sheet.type, function (data, lastModified) { - // Store data this session - session_cache += data.replace(/@import .+?;/ig, ''); - - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }, href); - } else { - // Use remote copy (re-parse) - try { - env.contents[href] = data; // Updating content cache - env.paths = [hrefParts.path]; - env.currentFileInfo = newFileInfo; - - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href); - //TODO - there must be a better way? A generic less-to-css function that can both call error - //and removeNode where appropriate - //should also add tests - if (env.currentFileInfo.rootFilename === href) { - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } - } catch (e) { - callback(e, null, null, sheet); - } - }); - } catch (e) { - callback(e, null, null, sheet); - } - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, null, sheet); - }); -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss == null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); - } - if (oldCss && keepOldCss === false) { - head.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] != undefined) { - error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} -})(window); diff --git a/dist/less-1.4.0.min.js b/dist/less-1.4.0.min.js deleted file mode 100644 index 4f0ee4f3a..000000000 --- a/dist/less-1.4.0.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * LESS - Leaner CSS v1.4.0 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache 2.0 License. - * - * @licence - */(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,i,s){e?k(e,i.href):t&&S(t.toCSS(r),i,s.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=b(t.href,e.location.href),u=o.url,a=l&&l.getItem(u),f=l&&l.getItem(u+":timestamp"),c={css:a,timestamp:f},h,p={relativeUrls:r.relativeUrls,currentDirectory:o.path,filename:u};t instanceof r.tree.parseEnv?(h=new r.tree.parseEnv(t),p.entryPath=h.currentFileInfo.entryPath,p.rootpath=h.currentFileInfo.rootpath,p.rootFilename=h.currentFileInfo.rootFilename):(h=new r.tree.parseEnv(r),h.mime=t.type,p.entryPath=o.path,p.rootpath=r.rootpath||o.path,p.rootFilename=u),h.relativeUrls&&(r.rootpath?p.rootpath=b(r.rootpath+y(o.path,p.entryPath)).path:p.rootpath=o.path),x(u,t.type,function(e,a){v+=e.replace(/@import .+?;/ig,"");if(!i&&c&&a&&(new Date(a)).valueOf()===(new Date(c.timestamp)).valueOf())S(c.css,t),n(null,null,e,t,{local:!0,remaining:s},u);else try{h.contents[u]=e,h.paths=[o.path],h.currentFileInfo=p,(new r.Parser(h)).parse(e,function(r,i){if(r)return n(r,null,null,t);try{n(r,i,e,t,{local:!1,lastModified:a,remaining:s},u),h.currentFileInfo.rootFilename===u&&N(document.getElementById("less-error-message:"+E(u)))}catch(r){n(r,null,null,t)}})}catch(f){n(f,null,null,t)}},function(e,r){n({type:"File",message:"'"+r+"' wasn't found ("+e+")"},null,null,t)})}function E(e){return e.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r=t.href||"",i="less:"+(t.title||E(r)),s=document.getElementById(i),o=!1,u=document.createElement("style");u.setAttribute("type","text/css"),t.media&&u.setAttribute("media",t.media),u.id=i;if(u.styleSheet)try{u.styleSheet.cssText=e}catch(a){throw new Error("Couldn't reassign styleSheet.cssText.")}else u.appendChild(document.createTextNode(e)),o=s!==null&&s.childNodes.length>0&&u.childNodes.length>0&&s.firstChild.nodeValue===u.firstChild.nodeValue;var f=document.getElementsByTagName("head")[0];if(s==null||o===!1){var c=t&&t.nextSibling||null;(c||document.getElementsByTagName("head")[0]).parentNode.insertBefore(u,c)}s&&o===!1&&f.removeChild(s);if(n&&l){C("saving "+r+" to cache.");try{l.setItem(r,e),l.setItem(r+":timestamp",n)}catch(a){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,n){var i="less-error-message:"+E(n||""),s='
  • {content}
  • ',o=document.createElement("div"),u,a,f=[],l=e.filename||n,c=l.match(/([^\/]+(\?.*)?)$/)[1];o.id=i,o.className="less-error-message",a="

    "+(e.type||"Syntax")+"Error: "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+c+" ";var h=function(e,n,r){e.extract[n]!=t&&f.push(s.replace(/\{line\}/,(parseInt(e.line)||0)+(n-1)).replace(/\{class\}/,r).replace(/\{content\}/,e.extract[n]))};e.extract?(h(e,0,""),h(e,1,"line"),h(e,2,""),a+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+f.join("")+"
    "):e.stack&&(a+="
    "+e.stack.split("\n").slice(1).join("
    ")),o.innerHTML=a,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),o.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(u=setInterval(function(){document.body&&(document.getElementById(i)?document.body.replaceChild(o,document.getElementById(i)):document.body.insertBefore(o,document.body.firstChild),clearInterval(u))},10))}var r,i,s;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof e=="undefined"?r={}:r=e.less={},i=r.tree={},r.mode="rhino"):typeof e=="undefined"?(r=exports,i=n("./tree"),r.mode="node"):(typeof e.less=="undefined"&&(e.less={}),r=e.less,i=e.less.tree={},r.mode="browser"),r.Parser=function(t){function m(){a=c[u],f=o,h=o}function g(){c[u]=a,o=f,h=o}function y(){o>h&&(c[u]=c[u].slice(o-h),h=o)}function b(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function w(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,y();else{y();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return E(r),typeof t=="string"?t:t.length===1?t[0]:t}function E(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function k(e,t,i){var s=i.currentFileInfo.filename;return r.mode!=="browser"&&r.mode!=="rhino"&&(s=n("path").resolve(s)),{lineNumber:C(e,t).line+1,fileName:s}}function L(e,t){var n=N(e,t),r=C(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.currentFileInfo.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&C(e.call,n).line+1,this.callExtract=o[C(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this;t instanceof i.parseEnv||(t=new i.parseEnv(t));var v=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n,i){var s=this;this.queue.push(e),r.Parser.importer(e,n,function(t,n,r){s.queue.splice(s.queue.indexOf(e),1);var o=r in s.files;s.files[r]=n,t&&!s.error&&(s.error=t),i(t,n,o)},t)}};return L.prototype=new Error,L.prototype.constructor=L,this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,p={imports:v,parse:function(e,a){var f,d,v,m,g,y,b=[],E,S=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.currentFileInfo.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(S)return a(new L(S,t));try{f=new i.Ruleset([],w(this.parsers.primary)),f.root=!0,f.firstRoot=!0}catch(x){return a(new L(x,t))}f.toCSS=function(e){var s,o,u;return function(s,o){s=s||{};var u,a=new i.evalEnv(s);typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),a.frames=[new i.Ruleset(null,o)]);try{var f=e.call(this,a);(new i.joinSelectorVisitor).run(f),(new i.processExtendsVisitor).run(f);var l=f.toCSS({compress:Boolean(s.compress),dumpLineNumbers:t.dumpLineNumbers,strictUnits:Boolean(s.strictUnits)})}catch(c){throw new L(c,t)}return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(l,s.maxLineLen):s.compress?l.replace(/(\s)+/g,"$1"):l}}(f.eval);if(o=0&&s.charAt(T)!=="\n";T--)N++;S={type:"Parse",message:"Unrecognised input",index:o,filename:t.currentFileInfo.filename,line:g,column:N,extract:[y[g-2],y[g-1],y[g]]}}var C=function(e){e=S||e||p.imports.error,e?(e instanceof L||(e=new L(e,t)),a(e)):a(null,f)};t.processImports!==!1?(new i.importVisitor(this.imports,C)).run(f):C()},parsers:{primary:function(){var e,t=[];while((e=w(this.extendRule)||w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/)||w(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(w(/^\/\/.*/),!0);if(e=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,n=o,r,u=o;s.charAt(n)==="~"&&(n++,r=!0);if(s.charAt(n)!=='"'&&s.charAt(n)!=="'")return;r&&w("~");if(e=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],r,u,t.currentFileInfo)},keyword:function(){var e;if(e=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=w(this.alpha);if(typeof s!="undefined")return s}w("("),r=w(this.entities.arguments);if(!w(")"))return;if(e)return new i.Call(e,r,a,t.currentFileInfo)},arguments:function(){var e=[],t;while(t=w(this.entities.assignment)||w(this.expression)){e.push(t);if(!w(","))break}return e},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)||w(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=w(/^\w+(?=\s?=)/i))&&w("=")&&(t=w(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!w(/^url\(/))return;return e=w(this.entities.quoted)||w(this.entities.variable)||w(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",S(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.currentFileInfo)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=w(/^@@?[\w-]+/)))return new i.Variable(e,n,t.currentFileInfo)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=w(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.currentFileInfo)},color:function(){var e;if(s.charAt(o)==="#"&&(e=w(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=w(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))return new i.Dimension(e[1],e[2])},unicodeDescriptor:function(){var e;if(e=w(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&w("~");if(e=w(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=w(/^(@[\w-]+)\s*:/)))return e[1]},extend:function(e){var t,n,r=o,s,u=[];if(!w(e?/^&:extend\(/:/^:extend\(/))return;do{s=null,t=[];for(;;){s=w(/^(all)(?=\s*(\)|,))/);if(s)break;n=w(this.element);if(!n)break;t.push(n)}s=s&&s[1],u.push(new i.Extend(new i.Selector(t),s,r))}while(w(","));return S(/^\)/),e&&S(/^;/),u},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var e=[],n,r,u,a,f,l=o,c=s.charAt(o),h=!1;if(c!=="."&&c!=="#")return;m();while(n=w(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=w(">");w("(")&&(u=this.mixin.args.call(this,!0).args,S(")")),u=u||[],w(this.important)&&(h=!0);if(e.length>0&&(w(";")||T("}")))return new i.mixin.Call(e,u,l,t.currentFileInfo,h);g()},args:function(e){var t=[],n=[],r,u=[],a,f,l,c,h,p={args:null,variadic:!1};for(;;){if(e)h=w(this.expression);else{w(this.comment);if(s.charAt(o)==="."&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({variadic:!0});break}h=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)}if(!h)break;l=null,h.throwAwayComments&&h.throwAwayComments(),c=h;var d=null;if(e){if(h.value.length==1)var d=h.value[0]}else d=h;if(d&&d instanceof i.Variable)if(w(":"))t.length>0&&(r&&x("Cannot mix ; and , as delimiter types"),a=!0),c=S(this.expression),l=f=d.name;else{if(!e&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({name:h.name,variadic:!0});break}e||(f=l=d.name,c=null)}c&&t.push(c),u.push({name:l,value:c});if(w(","))continue;if(w(";")||r)a&&x("Cannot mix ; and , as delimiter types"),r=!0,t.length>1&&(c=new i.Value(t)),n.push({name:f,value:c}),f=null,t=[],a=!1}return p.args=r?n:u,p},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||T(/^[^{]*\}/))return;m();if(n=w(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];var h=this.mixin.args.call(this,!1);t=h.args,c=h.variadic,w(")")||(l=o,g()),w(this.comment),w(/^when/)&&(f=S(this.conditions,"expected condition")),r=w(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);g()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||T("}")},alpha:function(){var e;if(!w(/^\(opacity=/i))return;if(e=w(/^\d+/)||w(this.entities.variable))return S(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=w(this.combinator),e=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||w("*")||w("&")||w(this.attribute)||w(/^\([^()@]+\)/)||w(/^[\.#](?=@)/)||w(this.entities.variableCurly),e||w("(")&&(r=w(this.selector))&&w(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e,t=s.charAt(o);if(t===">"||t==="+"||t==="~"||t==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(t)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u,a,f=[];while((a=w(this.extend))||(t=w(this.element))){a?f.push.apply(f,a):(f.length&&x("Extend can only be used at the end of selector"),r=s.charAt(o),n.push(t),t=null);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n,f);f.length&&x("Extend must be used to extend a selector, it cannot be used on its own")},attribute:function(){var e="",t,n,r;if(!w("["))return;(t=w(this.entities.variableCurly))||(t=S(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/));if(r=w(/^[|~*$^]?=/))n=w(this.entities.quoted)||w(/^[\w-]+/)||w(this.entities.variableCurly);return S("]"),new i.Attribute(t,r,n)},block:function(){var e;if(w("{")&&(e=w(this.primary))&&w("}"))return e},ruleset:function(){var e=[],n,r,u,a;m(),t.dumpLineNumbers&&(a=k(o,s,t));while(n=w(this.selector)){e.push(n),w(this.comment);if(!w(","))break;w(this.comment)}if(e.length>0&&(r=w(this.block))){var f=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(f.debugInfo=a),f}l=o,g()},rule:function(e){var n,r,u=s.charAt(o),a,c;m();if(u==="."||u==="#"||u==="&")return;if(n=w(this.variable)||w(this.property)){r=!e&&(t.compress||n.charAt(0)==="@")?w(this.value)||w(this.anonymousValue):w(this.anonymousValue)||w(this.value),a=w(this.important);if(r&&w(this.end))return new i.Rule(n,r,a,f,t.currentFileInfo);l=o,g();if(r&&!e)return this.rule(!0)}},anonymousValue:function(){if(match=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))return o+=match[0].length-1,new i.Anonymous(match[1])},"import":function(){var e,n,r=o;m();var s=w(/^@import?\s+/),u=(s?w(this.importOptions):null)||{};if(s&&(e=w(this.entities.quoted)||w(this.entities.url))){n=w(this.mediaFeatures);if(w(";"))return n=n&&new i.Value(n),new i.Import(e,n,u,r,t.currentFileInfo)}g()},importOptions:function(){var e,t={},n,r;if(!w("("))return null;do if(e=w(this.importOption)){n=e,r=!0;switch(n){case"css":n="less",r=!1;break;case"once":n="multiple",r=!1}t[n]=r;if(!w(","))break}while(e);return S(")"),t},importOption:function(){var e=w(/^(less|css|multiple|once)/);if(e)return e[1]},mediaFeature:function(){var e,n,r=[];do if(e=w(this.entities.keyword))r.push(e);else if(w("(")){n=w(this.property),e=w(this.value);if(!w(")"))return null;if(n&&e)r.push(new i.Paren(new i.Rule(n,e,null,o,t.currentFileInfo,!0)));else{if(!e)return null;r.push(new i.Paren(e))}}while(e);if(r.length>0)return new i.Expression(r)},mediaFeatures:function(){var e,t=[];do if(e=w(this.mediaFeature)){t.push(e);if(!w(","))break}else if(e=w(this.entities.variable)){t.push(e);if(!w(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=k(o,s,t));if(w(/^@media/)){e=w(this.mediaFeatures);if(n=w(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=w(this["import"])||w(this.media))return n;m(),e=w(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;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":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(w(/^[^{]+/)||"").trim());if(c){if(r=w(this.block))return new i.Directive(e,r)}else if((n=p?w(this.expression):w(this.entity))&&w(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=k(o,s,t)),d}g()},value:function(){var e,t=[],n;while(e=w(this.expression)){t.push(e);if(!w(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return w(/^! *important/)},sub:function(){var e,t;if(w("("))if(e=w(this.addition))return t=new i.Expression([e]),S(")"),t.parens=!0,t},multiplication:function(){var e,t,n,r,u,a=[];if(e=w(this.operand)){u=b(s.charAt(o-1));while(!T(/^\/[*\/]/)&&(n=w("/")||w("*"))){if(!(t=w(this.operand)))break;e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1))}return r||e}},addition:function(){var e,t,n,r,u;if(e=w(this.multiplication)){u=b(s.charAt(o-1));while((n=w(/^[-+]\s+/)||!u&&(w("+")||w("-")))&&(t=w(this.multiplication)))e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1));return r||e}},conditions:function(){var e,t,n=o,r;if(e=w(this.condition)){while(w(",")&&(t=w(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;w(/^not/)&&(u=!0),S("(");if(e=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(r=w(/^(?:>=|=<|[<=>])/))?(t=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):x("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),S(")"),w(/^and/)?new i.Condition("and",n,w(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=w("-"));var n=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return e&&(n.parensInOp=!0,n=new i.Negative(n)),n},expression:function(){var e,t,n=[],r;while(e=w(this.addition)||w(this.entity))n.push(e),!T(/^\/[\/*]/)&&(t=w("/"))&&n.push(new i.Anonymous(t));if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=w(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.currentDirectory&&(e=t.currentDirectory+e);var i=r.toSheet(e);i.processImports=!1,i.currentFileInfo=t,w(i,function(e,t,r,i,s,o){n.call(null,e,t,o)},!0)};(function(r){function u(e){return r.functions.hsla(e.h,e.s,e.l,e.a)}function a(e,t){return e instanceof r.Dimension&&e.unit.is("%")?parseFloat(e.value*t/100):f(e)}function f(e){if(e instanceof r.Dimension)return parseFloat(e.unit.is("%")?e.value/100:e.value);if(typeof e=="number")return e;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function l(e){return Math.min(1,Math.max(0,e))}r.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(e,t,n,i){var s=[e,t,n].map(function(e){return a(e,256)});return i=f(i),new r.Color(s,i)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,r){function o(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?s+(i-s)*e*6:e*2<1?i:e*3<2?s+(i-s)*(2/3-e)*6:s}e=f(e)%360/360,t=l(f(t)),n=l(f(n)),r=l(f(r));var i=n<=.5?n*(t+1):n+t-n*t,s=n*2-i;return this.rgba(o(e+1/3)*255,o(e)*255,o(e-1/3)*255,r)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,r){e=f(e)%360/360*360,t=f(t),n=f(n),r=f(r);var i,s;i=Math.floor(e/60%6),s=e/60-i;var o=[n,n*(1-t),n*(1-s*t),n*(1-(1-s)*t)],u=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(o[u[i][0]]*255,o[u[i][1]]*255,o[u[i][2]]*255,r)},hue:function(e){return new r.Dimension(Math.round(e.toHSL().h))},saturation:function(e){return new r.Dimension(Math.round(e.toHSL().s*100),"%")},lightness:function(e){return new r.Dimension(Math.round(e.toHSL().l*100),"%")},hsvhue:function(e){return new r.Dimension(Math.round(e.toHSV().h))},hsvsaturation:function(e){return new r.Dimension(Math.round(e.toHSV().s*100),"%")},hsvvalue:function(e){return new r.Dimension(Math.round(e.toHSV().v*100),"%")},red:function(e){return new r.Dimension(e.rgb[0])},green:function(e){return new r.Dimension(e.rgb[1])},blue:function(e){return new r.Dimension(e.rgb[2])},alpha:function(e){return new r.Dimension(e.toHSL().a)},luma:function(e){return new r.Dimension(Math.round(e.luma()*e.alpha*100),"%")},saturate:function(e,t){var n=e.toHSL();return n.s+=t.value/100,n.s=l(n.s),u(n)},desaturate:function(e,t){var n=e.toHSL();return n.s-=t.value/100,n.s=l(n.s),u(n)},lighten:function(e,t){var n=e.toHSL();return n.l+=t.value/100,n.l=l(n.l),u(n)},darken:function(e,t){var n=e.toHSL();return n.l-=t.value/100,n.l=l(n.l),u(n)},fadein:function(e,t){var n=e.toHSL();return n.a+=t.value/100,n.a=l(n.a),u(n)},fadeout:function(e,t){var n=e.toHSL();return n.a-=t.value/100,n.a=l(n.a),u(n)},fade:function(e,t){var n=e.toHSL();return n.a=t.value/100,n.a=l(n.a),u(n)},spin:function(e,t){var n=e.toHSL(),r=(n.h+t.value)%360;return n.h=r<0?360+r:r,u(n)},mix:function(e,t,n){n||(n=new r.Dimension(50));var i=n.value/100,s=i*2-1,o=e.toHSL().a-t.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[e.rgb[0]*u+t.rgb[0]*a,e.rgb[1]*u+t.rgb[1]*a,e.rgb[2]*u+t.rgb[2]*a],l=e.alpha*i+t.alpha*(1-i);return new r.Color(f,l)},greyscale:function(e){return this.desaturate(e,new r.Dimension(100))},contrast:function(e,t,n,r){if(!e.rgb)return null;typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1));if(t.luma()>n.luma()){var i=n;n=t,t=i}return typeof r=="undefined"?r=.43:r=f(r),e.luma()*e.alpha=d){if(this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",o,v,d),(new r.URL(i||t,this.currentFileInfo)).eval(this.env);this.env.silent||console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!",o,v,d)}p=f?p.toString("base64"):encodeURIComponent(p);var m="'data:"+s+","+p+"'";return new r.URL(new r.Anonymous(m))}},r._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(e){var i=n("path").extname(e),s=r._mime._types[i];if(s===t)throw new Error('Optional dependency "mime" is required for '+i);return s},charsets:{lookup:function(e){return e&&/^text\//.test(e)?"UTF-8":""}}};var i=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],s=function(e,t){return function(n){return t!=null&&(n=n.unify()),this._math(Math[e],t,n)}};for(var o=0;o255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("");return n&&(r=r.split(""),r[0]==r[1]&&r[2]==r[3]&&r[4]==r[5]?r=r[0]+r[2]+r[4]:r=r.join("")),"#"+r},operate:function(t,n,r){var i=[];r instanceof e.Color||(r=r.toColor());for(var s=0;s<3;s++)i[s]=e.operate(t,n,this.rgb[s],r.rgb[s]);return new e.Color(i,this.alpha+r.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={type:"Comment",toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype={type:"Condition",accept:function(e){this.lvalue=e.visit(this.lvalue),this.rvalue=e.visit(this.rvalue)},eval:function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}}(n("../tree")),function(e){e.Dimension=function(n,r){this.value=parseFloat(n),this.unit=r&&r instanceof e.Unit?r:new e.Unit(r?[r]:t)},e.Dimension.prototype={type:"Dimension",accept:function(e){this.unit=e.visit(this.unit)},eval:function(e){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(e){if(e&&e.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var t=this.value,n=String(t);t!==0&&t<1e-6&&t>-0.000001&&(n=t.toFixed(20).replace(/0+$/,""));if(e&&e.compress){if(t===0&&!this.unit.isAngle())return n;t>0&&t<1&&(n=n.substr(1))}return n+this.unit.toCSS(e)},operate:function(t,n,r){var i=e.operate(t,n,this.value,r.value),s=this.unit.clone();if(n==="+"||n==="-"){if(s.numerator.length===0&&s.denominator.length===0)s.numerator=r.unit.numerator.slice(0),s.denominator=r.unit.denominator.slice(0);else if(r.unit.numerator.length!=0||s.denominator.length!=0){r=r.convertTo(this.unit.usedUnits());if(t.strictUnits&&r.unit.toString()!==s.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+s.toString()+"' and '"+r.unit.toString()+"'.");i=e.operate(t,n,this.value,r.value)}}else n==="*"?(s.numerator=s.numerator.concat(r.unit.numerator).sort(),s.denominator=s.denominator.concat(r.unit.denominator).sort(),s.cancel()):n==="/"&&(s.numerator=s.numerator.concat(r.unit.denominator).sort(),s.denominator=s.denominator.concat(r.unit.numerator).sort(),s.cancel());return new e.Dimension(i,s)},compare:function(t){if(t instanceof e.Dimension){var n=this.unify(),r=t.unify(),i=n.value,s=r.value;return s>i?-1:s=1?this.numerator[0]:this.denominator.length>=1?this.denominator[0]:(!e||!e.strictUnits)&&this.backupUnit?this.backupUnit:""},toString:function(){var e,t=this.numerator.join("*");for(e=0;e0)for(n=0;n":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={type:"Expression",accept:function(e){this.value=e.visit(this.value)},eval:function(t){var n,r=this.parens&&!this.parensInOp,i=!1;return r&&t.inParenthesis(),this.value.length>1?n=new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?(this.value[0].parens&&!this.value[0].parensInOp&&(i=!0),n=this.value[0].eval(t)):n=this,r&&t.outOfParenthesis(),this.parens&&this.parensInOp&&!t.isMathOn()&&!i&&(n=new e.Paren(n)),n},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")},throwAwayComments:function(){this.value=this.value.filter(function(t){return!(t instanceof e.Comment)})}}}(n("../tree")),function(e){e.Extend=function(t,n,r){this.selector=t,this.option=n,this.index=r;switch(n){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}},e.Extend.prototype={type:"Extend",accept:function(e){this.selector=e.visit(this.selector)},eval:function(t){return new e.Extend(this.selector.eval(t),this.option,this.index)},clone:function(t){return new e.Extend(this.selector,this.option,this.index)},findSelfSelectors:function(e){var t=[];for(d=0;d1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t){var n=[],r=[],i=[],s=[],o,u,a;for(var f=0;f0){u=e.debugInfo(t,this),o=this.paths.map(function(e){return e.map(function(e){return e.toCSS(t)}).join("").trim()}).join(t.compress?",":",\n");for(var f=r.length-1;f>=0;f--)(r[f].slice(0,2)==="/*"||i.indexOf(r[f])===-1)&&i.unshift(r[f]);r=i,n.push(u+o+(t.compress?"{":" {\n ")+r.join(t.compress?"":"\n ")+(t.compress?"}":"\n}\n"))}return n.push(s),n.join("")+(t.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0),r.extendList),v=!1):d=new e.Selector([],r.extendList),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0&&t.push(a[i])},mergeElementsOnToSelectors:function(t,n){var r,i,s;if(n.length==0){n.push([new e.Selector(t)]);return}for(r=0;r0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t),i[i.length-1].extendList):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e,t){this.elements=e,this.extendList=t||[]},e.Selector.prototype={type:"Selector",accept:function(e){this.elements=e.visit(this.elements),this.extendList=e.visit(this.extendList)},match:function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree")),function(e){var t=["paths","optimization","files","contents","relativeUrls","strictImports","dumpLineNumbers","compress","processImports","mime","currentFileInfo"];e.parseEnv=function(e){r(e,this,t),this.contents||(this.contents={}),this.files||(this.files={});if(!this.currentFileInfo){var n=e.filename||"input";e.filename=null;var i=n.replace(/[^\/\\]*$/,"");this.currentFileInfo={filename:n,relativeUrls:this.relativeUrls,rootpath:e.rootpath||"",currentDirectory:i,entryPath:i,rootFilename:n}}},e.parseEnv.prototype.toSheet=function(t){var n=new e.parseEnv(this);return n.href=t,n.type=this.mime,n};var n=["silent","verbose","compress","ieCompat","strictMath","strictUnits"];e.evalEnv=function(e,t){r(e,this,n),this.frames=t||[]},e.evalEnv.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},e.evalEnv.prototype.outOfParenthesis=function(){this.parensStack.pop()},e.evalEnv.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},e.evalEnv.prototype.isPathRelative=function(e){return!/^(?:[a-z-]+:|\/)/.test(e)};var r=function(e,t,n){if(!e)return;for(var r=0;r100){var p="{unable to calculate}",d="{unable to calculate}";try{p=u[0].selfSelectors[0].toCSS(),d=u[0].selector.toCSS()}catch(v){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+p+":extend("+d+")"}}return u.concat(f.doExtendChaining(u,n,r+1))}return u},inInheritanceChain:function(e,t){if(e===t)return!0;if(t.parents){if(this.inInheritanceChain(e,t.parents[0]))return!0;if(this.inInheritanceChain(e,t.parents[1]))return!0}return!1},visitRule:function(e,t){t.visitDeeper=!1},visitMixinDefinition:function(e,t){t.visitDeeper=!1},visitSelector:function(e,t){t.visitDeeper=!1},visitRuleset:function(e,t){if(e.root)return;var n,r,i,s=this.allExtendsStack[this.allExtendsStack.length-1],o=[],u=this;for(i=0;i0&&f[c.matched].combinator.value!==o?c=null:c.matched++,c&&(c.finished=c.matched===f.length,c.finished&&!e.allowAfter&&(i+1i&&s>0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,match.pathIndex)),o.push(new e.Selector(a.elements.slice(s,match.index).concat([f]).concat(r.elements.slice(1)))),i=match.endPathIndex,s=match.endPathElementIndex,s>=a.elements.length&&(s=0,i++);return i0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,n.length)),o},visitRulesetOut:function(e){},visitMedia:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitMediaOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitDirectiveOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - options = options || {}; - var importError, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var evaldRoot = evaluate.call(this, evalEnv); - - new(tree.joinSelectorVisitor)() - .run(evaldRoot); - - new(tree.processExtendsVisitor)() - .run(evaldRoot); - - var css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css, options.maxLineLen); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - callback(e); - } - else { - callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")) - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, delim, arg, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new (tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, extend, extendList = []; - - while ((extend = $(this.extend)) || (e = $(this.element))) { - if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e) - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced, expression = []; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, currentFileInfo, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && currentFileInfo.currentDirectory) { - path = currentFileInfo.currentDirectory + path; - } - var sheetEnv = env.toSheet(path); - sheetEnv.processImports = false; - sheetEnv.currentFileInfo = currentFileInfo; - - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet(sheetEnv, - function (e, root, data, sheet, _, path) { - callback.call(null, e, root, path); - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - return values.value[index]; - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; - } - else { - useBase64 = /;base64$/.test(mimetype) - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } else if (!this.env.silent) { - // if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn - console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - }, - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - type: "Color", - eval: function () { return this }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - // - // 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. - // - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - - if (compress) { - color = color.split(''); - - // Convert color to short format - if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { - color = color[0] + color[2] + color[4]; - } else { - color = color.join(''); - } - } - - 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. - // - operate: function (env, op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - type: "Comment", - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function (env) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && !this.unit.isAngle()) { - return strValue; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - return strValue + this.unit.toCSS(env); - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, conversion, targetUnit, derivedConversions = {}; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(function (atomicUnit, denominator) { - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - - toCSS: function (env) { - if (this.numerator.length >= 1) { - return this.numerator[0]; - } - if (this.denominator.length >= 1) { - return this.denominator[0]; - } - if ((!env || !env.strictUnits) && this.backupUnit) { - return this.backupUnit; - } - return ""; - }, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isAngle: function () { - return tree.UnitConversions.angle.hasOwnProperty(this.toCSS()); - }, - - isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length == 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, groupName, result = {}; - - for (groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(function (atomicUnit) { - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - toCSS: function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - }, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i; - - for(i = 0; i < selectors.length; i++) { - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - var that = this; - - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined) { - this.css = !this.options.less; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - this.root = visitor.visit(this.root); - }, - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this.path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && !(path instanceof tree.URL)) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - toCSS: function () { return this.value; }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.ruleset = visitor.visit(this.ruleset); - }, - toCSS: function (env) { - var features = this.features.toCSS(env); - - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - toCSS: function () { return ""; }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval(new(tree.evalEnv)(env, - [this, frame].concat(mixinFrames))); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] - .concat(env.frames)))) { - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '-' + this.value.toCSS(env); - }, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { type: "Operation", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - toCSS: function (env) { - var separator = this.isSpaced ? " " : ""; - return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS(); - } -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '(' + this.value.toCSS(env).trim() + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.variable) { return "" } - else { - try { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - } - }, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && env.strictMath === false) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.index, this.currentFileInfo, this.inline); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - this.selectors = visitor.visit(this.selectors); - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - if (this.firstRoot && rule instanceof tree.Rule) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null}; - } - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - // Remove last semicolon - if (env.compress && rules.length) { - rule = rules[rules.length - 1]; - if (rule.charAt(rule.length - 1) === ';') { - rules[rules.length - 1] = rule.substring(0, rule.length - 1); - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = this.paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0), selector.extendList); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([], selector.extendList); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel, extendList; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements, extendList) { - this.elements = elements; - this.extendList = extendList || []; -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList) - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; - }, - eval: function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - })); - }, - toCSS: function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'strictImports', // option - - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'mime', // browser only - mime type for sheet import - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' = absolute path to the entry file - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - tree.parseEnv.prototype.toSheet = function (path) { - var env = new tree.parseEnv(this); - env.href = path; - //env.title = path; - env.type = this.mime; - return env; - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits' // whether units need to evaluate correctly - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - } -})(require('./tree'));(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - } - }; - -})(require('./tree'));(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode; - - if (!importNode.css) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && !evaldImportNode.css) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - if (imported && !importNode.options.multiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - } else { - subFinish(); - } - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree'));(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.ruleset.root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree'));(function (tree) { - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - } - } - - // now find every selector and apply the extends that apply to all extends - // and the ones which apply to an individual extend - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator == '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - return false; - }, - extendSelector:function (matches, selectorPath, replacementSelector) { - - //for a set of matches, replace each match with the replacement selector - - var currentSelectorPathIndex = 0, - currentSelectorPathElementIndex = 0, - path = [], - matchIndex, - selector, - firstElement, - match; - - for (matchIndex = 0; matchIndex < matches.length; matchIndex++) { - match = matches[matchIndex]; - selector = selectorPath[match.pathIndex]; - firstElement = new tree.Element( - match.initialCombinator, - replacementSelector.elements[0].value, - replacementSelector.elements[0].index - ); - - if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)) - )); - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selector.elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree'));// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -var cache = null; - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -var session_cache = ''; -less.modifyVars = function(record) { - var str = session_cache; - for (var name in record) { - str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - new(less.Parser)(new less.tree.parseEnv(less)).parse(str, function (e, root) { - if (e) { - error(e, "session_cache"); - } else { - createCSS(root.toCSS(less), less.sheets[less.sheets.length - 1]); - } - }); -}; - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - var env = new less.tree.parseEnv(less); - env.filename = document.location.href.replace(/#.*$/, ''); - - new(less.Parser)(env).parse(styles[i].innerHTML || '', function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace("\\", "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(sheet.href, window.location.href); - var href = hrefParts.url; - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - var env; - var newFileInfo = { - relativeUrls: less.relativeUrls, - currentDirectory: hrefParts.path, - filename: href - }; - - if (sheet instanceof less.tree.parseEnv) { - env = new less.tree.parseEnv(sheet); - newFileInfo.entryPath = env.currentFileInfo.entryPath; - newFileInfo.rootpath = env.currentFileInfo.rootpath; - newFileInfo.rootFilename = env.currentFileInfo.rootFilename; - } else { - env = new less.tree.parseEnv(less); - env.mime = sheet.type; - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - } - - if (env.relativeUrls) { - //todo - this relies on option being set on less object rather than being passed in as an option - // - need an originalRootpath - if (less.rootpath) { - newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - xhr(href, sheet.type, function (data, lastModified) { - // Store data this session - session_cache += data.replace(/@import .+?;/ig, ''); - - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }, href); - } else { - // Use remote copy (re-parse) - try { - env.contents[href] = data; // Updating content cache - env.paths = [hrefParts.path]; - env.currentFileInfo = newFileInfo; - - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href); - //TODO - there must be a better way? A generic less-to-css function that can both call error - //and removeNode where appropriate - //should also add tests - if (env.currentFileInfo.rootFilename === href) { - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } - } catch (e) { - callback(e, null, null, sheet); - } - }); - } catch (e) { - callback(e, null, null, sheet); - } - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, null, sheet); - }); -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss == null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); - } - if (oldCss && keepOldCss === false) { - head.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] != undefined) { - error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} -})(window); diff --git a/dist/less-1.4.1.min.js b/dist/less-1.4.1.min.js deleted file mode 100644 index 5aaea85d3..000000000 --- a/dist/less-1.4.1.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * LESS - Leaner CSS v1.4.1 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache 2.0 License. - * - * @licence - */(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,i,s){e?k(e,i.href):t&&S(t.toCSS(r),i,s.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=b(t.href,e.location.href),u=o.url,a=l&&l.getItem(u),f=l&&l.getItem(u+":timestamp"),c={css:a,timestamp:f},h,p={relativeUrls:r.relativeUrls,currentDirectory:o.path,filename:u};t instanceof r.tree.parseEnv?(h=new r.tree.parseEnv(t),p.entryPath=h.currentFileInfo.entryPath,p.rootpath=h.currentFileInfo.rootpath,p.rootFilename=h.currentFileInfo.rootFilename):(h=new r.tree.parseEnv(r),h.mime=t.type,p.entryPath=o.path,p.rootpath=r.rootpath||o.path,p.rootFilename=u),h.relativeUrls&&(r.rootpath?p.rootpath=b(r.rootpath+y(o.path,p.entryPath)).path:p.rootpath=o.path),x(u,t.type,function(e,a){v+=e.replace(/@import .+?;/ig,"");if(!i&&c&&a&&(new Date(a)).valueOf()===(new Date(c.timestamp)).valueOf())S(c.css,t),n(null,null,e,t,{local:!0,remaining:s},u);else try{h.contents[u]=e,h.paths=[o.path],h.currentFileInfo=p,(new r.Parser(h)).parse(e,function(r,i){if(r)return n(r,null,null,t);try{n(r,i,e,t,{local:!1,lastModified:a,remaining:s},u),h.currentFileInfo.rootFilename===u&&N(document.getElementById("less-error-message:"+E(u)))}catch(r){n(r,null,null,t)}})}catch(f){n(f,null,null,t)}},function(e,r){n({type:"File",message:"'"+r+"' wasn't found ("+e+")"},null,null,t)})}function E(e){return e.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r=t.href||"",i="less:"+(t.title||E(r)),s=document.getElementById(i),o=!1,u=document.createElement("style");u.setAttribute("type","text/css"),t.media&&u.setAttribute("media",t.media),u.id=i;if(u.styleSheet)try{u.styleSheet.cssText=e}catch(a){throw new Error("Couldn't reassign styleSheet.cssText.")}else u.appendChild(document.createTextNode(e)),o=s!==null&&s.childNodes.length>0&&u.childNodes.length>0&&s.firstChild.nodeValue===u.firstChild.nodeValue;var f=document.getElementsByTagName("head")[0];if(s==null||o===!1){var c=t&&t.nextSibling||null;(c||document.getElementsByTagName("head")[0]).parentNode.insertBefore(u,c)}s&&o===!1&&f.removeChild(s);if(n&&l){C("saving "+r+" to cache.");try{l.setItem(r,e),l.setItem(r+":timestamp",n)}catch(a){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,n){var i="less-error-message:"+E(n||""),s='
  • {content}
  • ',o=document.createElement("div"),u,a,f=[],l=e.filename||n,c=l.match(/([^\/]+(\?.*)?)$/)[1];o.id=i,o.className="less-error-message",a="

    "+(e.type||"Syntax")+"Error: "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+c+" ";var h=function(e,n,r){e.extract[n]!=t&&f.push(s.replace(/\{line\}/,(parseInt(e.line)||0)+(n-1)).replace(/\{class\}/,r).replace(/\{content\}/,e.extract[n]))};e.extract?(h(e,0,""),h(e,1,"line"),h(e,2,""),a+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+f.join("")+"
    "):e.stack&&(a+="
    "+e.stack.split("\n").slice(1).join("
    ")),o.innerHTML=a,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),o.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(u=setInterval(function(){document.body&&(document.getElementById(i)?document.body.replaceChild(o,document.getElementById(i)):document.body.insertBefore(o,document.body.firstChild),clearInterval(u))},10))}var r,i,s;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof e=="undefined"?r={}:r=e.less={},i=r.tree={},r.mode="rhino"):typeof e=="undefined"?(r=exports,i=n("./tree"),r.mode="node"):(typeof e.less=="undefined"&&(e.less={}),r=e.less,i=e.less.tree={},r.mode="browser"),r.Parser=function(t){function m(){a=c[u],f=o,h=o}function g(){c[u]=a,o=f,h=o}function y(){o>h&&(c[u]=c[u].slice(o-h),h=o)}function b(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function w(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,y();else{y();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return E(r),typeof t=="string"?t:t.length===1?t[0]:t}function E(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function k(e,t,i){var s=i.currentFileInfo.filename;return r.mode!=="browser"&&r.mode!=="rhino"&&(s=n("path").resolve(s)),{lineNumber:C(e,t).line+1,fileName:s}}function L(e,t){var n=N(e,t),r=C(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.currentFileInfo.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&C(e.call,n).line+1,this.callExtract=o[C(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this;t instanceof i.parseEnv||(t=new i.parseEnv(t));var v=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n,i){var s=this;this.queue.push(e),r.Parser.importer(e,n,function(t,n,r){s.queue.splice(s.queue.indexOf(e),1);var o=r in s.files;s.files[r]=n,t&&!s.error&&(s.error=t),i(t,n,o)},t)}};return L.prototype=new Error,L.prototype.constructor=L,this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,p={imports:v,parse:function(e,a){var f,d,v,m,g,y,b=[],E,S=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.currentFileInfo.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(S)return a(new L(S,t));try{f=new i.Ruleset([],w(this.parsers.primary)),f.root=!0,f.firstRoot=!0}catch(x){return a(new L(x,t))}f.toCSS=function(e){var s,o,u;return function(s,o){s=s||{};var u,a=new i.evalEnv(s);typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),a.frames=[new i.Ruleset(null,o)]);try{var f=e.call(this,a);(new i.joinSelectorVisitor).run(f),(new i.processExtendsVisitor).run(f);var l=f.toCSS({compress:Boolean(s.compress),dumpLineNumbers:t.dumpLineNumbers,strictUnits:Boolean(s.strictUnits)})}catch(c){throw new L(c,t)}return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(l,s.maxLineLen):s.compress?l.replace(/(\s)+/g,"$1"):l}}(f.eval);if(o=0&&s.charAt(T)!=="\n";T--)N++;S={type:"Parse",message:"Unrecognised input",index:o,filename:t.currentFileInfo.filename,line:g,column:N,extract:[y[g-2],y[g-1],y[g]]}}var C=function(e){e=S||e||p.imports.error,e?(e instanceof L||(e=new L(e,t)),a(e)):a(null,f)};t.processImports!==!1?(new i.importVisitor(this.imports,C)).run(f):C()},parsers:{primary:function(){var e,t=[];while((e=w(this.extendRule)||w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/)||w(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(w(/^\/\/.*/),!0);if(e=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,n=o,r,u=o;s.charAt(n)==="~"&&(n++,r=!0);if(s.charAt(n)!=='"'&&s.charAt(n)!=="'")return;r&&w("~");if(e=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],r,u,t.currentFileInfo)},keyword:function(){var e;if(e=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=w(this.alpha);if(typeof s!="undefined")return s}w("("),r=w(this.entities.arguments);if(!w(")"))return;if(e)return new i.Call(e,r,a,t.currentFileInfo)},arguments:function(){var e=[],t;while(t=w(this.entities.assignment)||w(this.expression)){e.push(t);if(!w(","))break}return e},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)||w(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=w(/^\w+(?=\s?=)/i))&&w("=")&&(t=w(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!w(/^url\(/))return;return e=w(this.entities.quoted)||w(this.entities.variable)||w(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",S(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.currentFileInfo)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=w(/^@@?[\w-]+/)))return new i.Variable(e,n,t.currentFileInfo)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=w(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.currentFileInfo)},color:function(){var e;if(s.charAt(o)==="#"&&(e=w(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=w(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))return new i.Dimension(e[1],e[2])},unicodeDescriptor:function(){var e;if(e=w(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&w("~");if(e=w(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=w(/^(@[\w-]+)\s*:/)))return e[1]},extend:function(e){var t,n,r=o,s,u=[];if(!w(e?/^&:extend\(/:/^:extend\(/))return;do{s=null,t=[];for(;;){s=w(/^(all)(?=\s*(\)|,))/);if(s)break;n=w(this.element);if(!n)break;t.push(n)}s=s&&s[1],u.push(new i.Extend(new i.Selector(t),s,r))}while(w(","));return S(/^\)/),e&&S(/^;/),u},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var e=[],n,r,u,a,f,l=o,c=s.charAt(o),h=!1;if(c!=="."&&c!=="#")return;m();while(n=w(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=w(">");w("(")&&(u=this.mixin.args.call(this,!0).args,S(")")),u=u||[],w(this.important)&&(h=!0);if(e.length>0&&(w(";")||T("}")))return new i.mixin.Call(e,u,l,t.currentFileInfo,h);g()},args:function(e){var t=[],n=[],r,u=[],a,f,l,c,h,p={args:null,variadic:!1};for(;;){if(e)h=w(this.expression);else{w(this.comment);if(s.charAt(o)==="."&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({variadic:!0});break}h=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)}if(!h)break;l=null,h.throwAwayComments&&h.throwAwayComments(),c=h;var d=null;if(e){if(h.value.length==1)var d=h.value[0]}else d=h;if(d&&d instanceof i.Variable)if(w(":"))t.length>0&&(r&&x("Cannot mix ; and , as delimiter types"),a=!0),c=S(this.expression),l=f=d.name;else{if(!e&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({name:h.name,variadic:!0});break}e||(f=l=d.name,c=null)}c&&t.push(c),u.push({name:l,value:c});if(w(","))continue;if(w(";")||r)a&&x("Cannot mix ; and , as delimiter types"),r=!0,t.length>1&&(c=new i.Value(t)),n.push({name:f,value:c}),f=null,t=[],a=!1}return p.args=r?n:u,p},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||T(/^[^{]*\}/))return;m();if(n=w(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];var h=this.mixin.args.call(this,!1);t=h.args,c=h.variadic,w(")")||(l=o,g()),w(this.comment),w(/^when/)&&(f=S(this.conditions,"expected condition")),r=w(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);g()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||T("}")},alpha:function(){var e;if(!w(/^\(opacity=/i))return;if(e=w(/^\d+/)||w(this.entities.variable))return S(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=w(this.combinator),e=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||w("*")||w("&")||w(this.attribute)||w(/^\([^()@]+\)/)||w(/^[\.#](?=@)/)||w(this.entities.variableCurly),e||w("(")&&(r=w(this.selector))&&w(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e=s.charAt(o);if(e===">"||e==="+"||e==="~"||e==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(e)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u,a=[];while((u=w(this.extend))||(t=w(this.element))){u?a.push.apply(a,u):(a.length&&x("Extend can only be used at the end of selector"),r=s.charAt(o),n.push(t),t=null);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n,a);a.length&&x("Extend must be used to extend a selector, it cannot be used on its own")},attribute:function(){var e="",t,n,r;if(!w("["))return;(t=w(this.entities.variableCurly))||(t=S(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/));if(r=w(/^[|~*$^]?=/))n=w(this.entities.quoted)||w(/^[\w-]+/)||w(this.entities.variableCurly);return S("]"),new i.Attribute(t,r,n)},block:function(){var e;if(w("{")&&(e=w(this.primary))&&w("}"))return e},ruleset:function(){var e=[],n,r,u;m(),t.dumpLineNumbers&&(u=k(o,s,t));while(n=w(this.selector)){e.push(n),w(this.comment);if(!w(","))break;w(this.comment)}if(e.length>0&&(r=w(this.block))){var a=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(a.debugInfo=u),a}l=o,g()},rule:function(e){var n,r,u=s.charAt(o),a;m();if(u==="."||u==="#"||u==="&")return;if(n=w(this.variable)||w(this.property)){r=!e&&(t.compress||n.charAt(0)==="@")?w(this.value)||w(this.anonymousValue):w(this.anonymousValue)||w(this.value),a=w(this.important);if(r&&w(this.end))return new i.Rule(n,r,a,f,t.currentFileInfo);l=o,g();if(r&&!e)return this.rule(!0)}},anonymousValue:function(){var e;if(e=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))return o+=e[0].length-1,new i.Anonymous(e[1])},"import":function(){var e,n,r=o;m();var s=w(/^@import?\s+/),u=(s?w(this.importOptions):null)||{};if(s&&(e=w(this.entities.quoted)||w(this.entities.url))){n=w(this.mediaFeatures);if(w(";"))return n=n&&new i.Value(n),new i.Import(e,n,u,r,t.currentFileInfo)}g()},importOptions:function(){var e,t={},n,r;if(!w("("))return null;do if(e=w(this.importOption)){n=e,r=!0;switch(n){case"css":n="less",r=!1;break;case"once":n="multiple",r=!1}t[n]=r;if(!w(","))break}while(e);return S(")"),t},importOption:function(){var e=w(/^(less|css|multiple|once)/);if(e)return e[1]},mediaFeature:function(){var e,n,r=[];do if(e=w(this.entities.keyword))r.push(e);else if(w("(")){n=w(this.property),e=w(this.value);if(!w(")"))return null;if(n&&e)r.push(new i.Paren(new i.Rule(n,e,null,o,t.currentFileInfo,!0)));else{if(!e)return null;r.push(new i.Paren(e))}}while(e);if(r.length>0)return new i.Expression(r)},mediaFeatures:function(){var e,t=[];do if(e=w(this.mediaFeature)){t.push(e);if(!w(","))break}else if(e=w(this.entities.variable)){t.push(e);if(!w(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=k(o,s,t));if(w(/^@media/)){e=w(this.mediaFeatures);if(n=w(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=w(this["import"])||w(this.media))return n;m(),e=w(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;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":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(w(/^[^{]+/)||"").trim());if(c){if(r=w(this.block))return new i.Directive(e,r)}else if((n=p?w(this.expression):w(this.entity))&&w(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=k(o,s,t)),d}g()},value:function(){var e,t=[],n;while(e=w(this.expression)){t.push(e);if(!w(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return w(/^! *important/)},sub:function(){var e,t;if(w("("))if(e=w(this.addition))return t=new i.Expression([e]),S(")"),t.parens=!0,t},multiplication:function(){var e,t,n,r,u,a=[];if(e=w(this.operand)){u=b(s.charAt(o-1));while(!T(/^\/[*\/]/)&&(n=w("/")||w("*"))){if(!(t=w(this.operand)))break;e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1))}return r||e}},addition:function(){var e,t,n,r,u;if(e=w(this.multiplication)){u=b(s.charAt(o-1));while((n=w(/^[-+]\s+/)||!u&&(w("+")||w("-")))&&(t=w(this.multiplication)))e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1));return r||e}},conditions:function(){var e,t,n=o,r;if(e=w(this.condition)){while(w(",")&&(t=w(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;w(/^not/)&&(u=!0),S("(");if(e=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(r=w(/^(?:>=|=<|[<=>])/))?(t=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):x("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),S(")"),w(/^and/)?new i.Condition("and",n,w(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=w("-"));var n=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return e&&(n.parensInOp=!0,n=new i.Negative(n)),n},expression:function(){var e,t,n=[],r;while(e=w(this.addition)||w(this.entity))n.push(e),!T(/^\/[\/*]/)&&(t=w("/"))&&n.push(new i.Anonymous(t));if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=w(/^(\*?-?[_a-z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.currentDirectory&&(e=t.currentDirectory+e);var i=r.toSheet(e);i.processImports=!1,i.currentFileInfo=t,w(i,function(e,t,r,i,s,o){n.call(null,e,t,o)},!0)};(function(r){function u(e){return r.functions.hsla(e.h,e.s,e.l,e.a)}function a(e,t){return e instanceof r.Dimension&&e.unit.is("%")?parseFloat(e.value*t/100):f(e)}function f(e){if(e instanceof r.Dimension)return parseFloat(e.unit.is("%")?e.value/100:e.value);if(typeof e=="number")return e;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function l(e){return Math.min(1,Math.max(0,e))}r.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(e,t,n,i){var s=[e,t,n].map(function(e){return a(e,256)});return i=f(i),new r.Color(s,i)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,r){function o(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?s+(i-s)*e*6:e*2<1?i:e*3<2?s+(i-s)*(2/3-e)*6:s}e=f(e)%360/360,t=l(f(t)),n=l(f(n)),r=l(f(r));var i=n<=.5?n*(t+1):n+t-n*t,s=n*2-i;return this.rgba(o(e+1/3)*255,o(e)*255,o(e-1/3)*255,r)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,r){e=f(e)%360/360*360,t=f(t),n=f(n),r=f(r);var i,s;i=Math.floor(e/60%6),s=e/60-i;var o=[n,n*(1-t),n*(1-s*t),n*(1-(1-s)*t)],u=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(o[u[i][0]]*255,o[u[i][1]]*255,o[u[i][2]]*255,r)},hue:function(e){return new r.Dimension(Math.round(e.toHSL().h))},saturation:function(e){return new r.Dimension(Math.round(e.toHSL().s*100),"%")},lightness:function(e){return new r.Dimension(Math.round(e.toHSL().l*100),"%")},hsvhue:function(e){return new r.Dimension(Math.round(e.toHSV().h))},hsvsaturation:function(e){return new r.Dimension(Math.round(e.toHSV().s*100),"%")},hsvvalue:function(e){return new r.Dimension(Math.round(e.toHSV().v*100),"%")},red:function(e){return new r.Dimension(e.rgb[0])},green:function(e){return new r.Dimension(e.rgb[1])},blue:function(e){return new r.Dimension(e.rgb[2])},alpha:function(e){return new r.Dimension(e.toHSL().a)},luma:function(e){return new r.Dimension(Math.round(e.luma()*e.alpha*100),"%")},saturate:function(e,t){var n=e.toHSL();return n.s+=t.value/100,n.s=l(n.s),u(n)},desaturate:function(e,t){var n=e.toHSL();return n.s-=t.value/100,n.s=l(n.s),u(n)},lighten:function(e,t){var n=e.toHSL();return n.l+=t.value/100,n.l=l(n.l),u(n)},darken:function(e,t){var n=e.toHSL();return n.l-=t.value/100,n.l=l(n.l),u(n)},fadein:function(e,t){var n=e.toHSL();return n.a+=t.value/100,n.a=l(n.a),u(n)},fadeout:function(e,t){var n=e.toHSL();return n.a-=t.value/100,n.a=l(n.a),u(n)},fade:function(e,t){var n=e.toHSL();return n.a=t.value/100,n.a=l(n.a),u(n)},spin:function(e,t){var n=e.toHSL(),r=(n.h+t.value)%360;return n.h=r<0?360+r:r,u(n)},mix:function(e,t,n){n||(n=new r.Dimension(50));var i=n.value/100,s=i*2-1,o=e.toHSL().a-t.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[e.rgb[0]*u+t.rgb[0]*a,e.rgb[1]*u+t.rgb[1]*a,e.rgb[2]*u+t.rgb[2]*a],l=e.alpha*i+t.alpha*(1-i);return new r.Color(f,l)},greyscale:function(e){return this.desaturate(e,new r.Dimension(100))},contrast:function(e,t,n,r){if(!e.rgb)return null;typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1));if(t.luma()>n.luma()){var i=n;n=t,t=i}return typeof r=="undefined"?r=.43:r=f(r),e.luma()*e.alpha=d){if(this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",o,v,d),(new r.URL(i||t,this.currentFileInfo)).eval(this.env);this.env.silent||console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!",o,v,d)}p=f?p.toString("base64"):encodeURIComponent(p);var m="'data:"+s+","+p+"'";return new r.URL(new r.Anonymous(m))}},r._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(e){var i=n("path").extname(e),s=r._mime._types[i];if(s===t)throw new Error('Optional dependency "mime" is required for '+i);return s},charsets:{lookup:function(e){return e&&/^text\//.test(e)?"UTF-8":""}}};var i=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],s=function(e,t){return function(n){return t!=null&&(n=n.unify()),this._math(Math[e],t,n)}};for(var o=0;o255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("");return n&&(r=r.split(""),r[0]==r[1]&&r[2]==r[3]&&r[4]==r[5]?r=r[0]+r[2]+r[4]:r=r.join("")),"#"+r},operate:function(t,n,r){var i=[];r instanceof e.Color||(r=r.toColor());for(var s=0;s<3;s++)i[s]=e.operate(t,n,this.rgb[s],r.rgb[s]);return new e.Color(i,this.alpha+r.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={type:"Comment",toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype={type:"Condition",accept:function(e){this.lvalue=e.visit(this.lvalue),this.rvalue=e.visit(this.rvalue)},eval:function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}}(n("../tree")),function(e){e.Dimension=function(n,r){this.value=parseFloat(n),this.unit=r&&r instanceof e.Unit?r:new e.Unit(r?[r]:t)},e.Dimension.prototype={type:"Dimension",accept:function(e){this.unit=e.visit(this.unit)},eval:function(e){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(e){if(e&&e.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var t=this.value,n=String(t);t!==0&&t<1e-6&&t>-0.000001&&(n=t.toFixed(20).replace(/0+$/,""));if(e&&e.compress){if(t===0&&!this.unit.isAngle())return n;t>0&&t<1&&(n=n.substr(1))}return n+this.unit.toCSS(e)},operate:function(t,n,r){var i=e.operate(t,n,this.value,r.value),s=this.unit.clone();if(n==="+"||n==="-"){if(s.numerator.length===0&&s.denominator.length===0)s.numerator=r.unit.numerator.slice(0),s.denominator=r.unit.denominator.slice(0);else if(r.unit.numerator.length!=0||s.denominator.length!=0){r=r.convertTo(this.unit.usedUnits());if(t.strictUnits&&r.unit.toString()!==s.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+s.toString()+"' and '"+r.unit.toString()+"'.");i=e.operate(t,n,this.value,r.value)}}else n==="*"?(s.numerator=s.numerator.concat(r.unit.numerator).sort(),s.denominator=s.denominator.concat(r.unit.denominator).sort(),s.cancel()):n==="/"&&(s.numerator=s.numerator.concat(r.unit.denominator).sort(),s.denominator=s.denominator.concat(r.unit.numerator).sort(),s.cancel());return new e.Dimension(i,s)},compare:function(t){if(t instanceof e.Dimension){var n=this.unify(),r=t.unify(),i=n.value,s=r.value;return s>i?-1:s=1?this.numerator[0]:this.denominator.length>=1?this.denominator[0]:(!e||!e.strictUnits)&&this.backupUnit?this.backupUnit:""},toString:function(){var e,t=this.numerator.join("*");for(e=0;e0)for(n=0;n":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={type:"Expression",accept:function(e){this.value=e.visit(this.value)},eval:function(t){var n,r=this.parens&&!this.parensInOp,i=!1;return r&&t.inParenthesis(),this.value.length>1?n=new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?(this.value[0].parens&&!this.value[0].parensInOp&&(i=!0),n=this.value[0].eval(t)):n=this,r&&t.outOfParenthesis(),this.parens&&this.parensInOp&&!t.isMathOn()&&!i&&(n=new e.Paren(n)),n},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")},throwAwayComments:function(){this.value=this.value.filter(function(t){return!(t instanceof e.Comment)})}}}(n("../tree")),function(e){e.Extend=function(t,n,r){this.selector=t,this.option=n,this.index=r;switch(n){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}},e.Extend.prototype={type:"Extend",accept:function(e){this.selector=e.visit(this.selector)},eval:function(t){return new e.Extend(this.selector.eval(t),this.option,this.index)},clone:function(t){return new e.Extend(this.selector,this.option,this.index)},findSelfSelectors:function(e){var t=[],n;for(n=0;n1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t){var n=[],r=[],i=[],s=[],o,u,a;for(var f=0;f0){u=e.debugInfo(t,this),o=this.paths.map(function(e){return e.map(function(e){return e.toCSS(t)}).join("").trim()}).join(t.compress?",":",\n");for(var f=r.length-1;f>=0;f--)(r[f].slice(0,2)==="/*"||i.indexOf(r[f])===-1)&&i.unshift(r[f]);r=i,n.push(u+o+(t.compress?"{":" {\n ")+r.join(t.compress?"":"\n ")+(t.compress?"}":"\n}\n"))}return n.push(s),n.join("")+(t.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0),r.extendList),v=!1):d=new e.Selector([],r.extendList),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0&&t.push(a[i])},mergeElementsOnToSelectors:function(t,n){var r,i,s;if(n.length==0){n.push([new e.Selector(t)]);return}for(r=0;r0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t),i[i.length-1].extendList):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e,t){this.elements=e,this.extendList=t||[]},e.Selector.prototype={type:"Selector",accept:function(e){this.elements=e.visit(this.elements),this.extendList=e.visit(this.extendList)},match:function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree")),function(e){var t=["paths","optimization","files","contents","relativeUrls","strictImports","dumpLineNumbers","compress","processImports","syncImport","mime","currentFileInfo"];e.parseEnv=function(e){r(e,this,t),this.contents||(this.contents={}),this.files||(this.files={});if(!this.currentFileInfo){var n=e&&e.filename||"input",i=n.replace(/[^\/\\]*$/,"");e&&(e.filename=null),this.currentFileInfo={filename:n,relativeUrls:this.relativeUrls,rootpath:e&&e.rootpath||"",currentDirectory:i,entryPath:i,rootFilename:n}}},e.parseEnv.prototype.toSheet=function(t){var n=new e.parseEnv(this);return n.href=t,n.type=this.mime,n};var n=["silent","verbose","compress","yuicompress","ieCompat","strictMath","strictUnits"];e.evalEnv=function(e,t){r(e,this,n),this.frames=t||[]},e.evalEnv.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},e.evalEnv.prototype.outOfParenthesis=function(){this.parensStack.pop()},e.evalEnv.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},e.evalEnv.prototype.isPathRelative=function(e){return!/^(?:[a-z-]+:|\/)/.test(e)};var r=function(e,t,n){if(!e)return;for(var r=0;r100){var d="{unable to calculate}",v="{unable to calculate}";try{d=u[0].selfSelectors[0].toCSS(),v=u[0].selector.toCSS()}catch(m){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+d+":extend("+v+")"}}return u.concat(f.doExtendChaining(u,n,r+1))}return u},inInheritanceChain:function(e,t){if(e===t)return!0;if(t.parents){if(this.inInheritanceChain(e,t.parents[0]))return!0;if(this.inInheritanceChain(e,t.parents[1]))return!0}return!1},visitRule:function(e,t){t.visitDeeper=!1},visitMixinDefinition:function(e,t){t.visitDeeper=!1},visitSelector:function(e,t){t.visitDeeper=!1},visitRuleset:function(e,t){if(e.root)return;var n,r,i,s=this.allExtendsStack[this.allExtendsStack.length-1],o=[],u=this,a;for(i=0;i0&&f[c.matched].combinator.value!==o?c=null:c.matched++,c&&(c.finished=c.matched===f.length,c.finished&&!e.allowAfter&&(i+1i&&s>0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,l.pathIndex)),o.push(new e.Selector(a.elements.slice(s,l.index).concat([f]).concat(r.elements.slice(1)))),i=l.endPathIndex,s=l.endPathElementIndex,s>=a.elements.length&&(s=0,i++);return i0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,n.length)),o},visitRulesetOut:function(e){},visitMedia:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitMediaOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitDirectiveOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - options = options || {}; - var importError, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var evaldRoot = evaluate.call(this, evalEnv); - - new(tree.joinSelectorVisitor)() - .run(evaldRoot); - - new(tree.processExtendsVisitor)() - .run(evaldRoot); - - var css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css, options.maxLineLen); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - callback(e); - } - else { - callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")) - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, delim, arg, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new (tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, extend, extendList = []; - - while ((extend = $(this.extend)) || (e = $(this.element))) { - if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e) - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced, expression = []; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, currentFileInfo, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && currentFileInfo.currentDirectory) { - path = currentFileInfo.currentDirectory + path; - } - var sheetEnv = env.toSheet(path); - sheetEnv.processImports = false; - sheetEnv.currentFileInfo = currentFileInfo; - - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet(sheetEnv, - function (e, root, data, sheet, _, path) { - callback.call(null, e, root, path); - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - return values.value[index]; - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; - } - else { - useBase64 = /;base64$/.test(mimetype) - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } else if (!this.env.silent) { - // if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn - console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - }, - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - type: "Color", - eval: function () { return this }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - // - // 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. - // - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - - if (compress) { - color = color.split(''); - - // Convert color to short format - if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) { - color = color[0] + color[2] + color[4]; - } else { - color = color.join(''); - } - } - - 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. - // - operate: function (env, op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - type: "Comment", - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function (env) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && !this.unit.isAngle()) { - return strValue; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - return strValue + this.unit.toCSS(env); - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, conversion, targetUnit, derivedConversions = {}; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(function (atomicUnit, denominator) { - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - - toCSS: function (env) { - if (this.numerator.length >= 1) { - return this.numerator[0]; - } - if (this.denominator.length >= 1) { - return this.denominator[0]; - } - if ((!env || !env.strictUnits) && this.backupUnit) { - return this.backupUnit; - } - return ""; - }, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isAngle: function () { - return tree.UnitConversions.angle.hasOwnProperty(this.toCSS()); - }, - - isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length == 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, groupName, result = {}; - - for (groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(function (atomicUnit) { - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - toCSS: function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - }, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i; - - for(i = 0; i < selectors.length; i++) { - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - var that = this; - - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined) { - this.css = !this.options.less; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - this.root = visitor.visit(this.root); - }, - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this.path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && !(path instanceof tree.URL)) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - toCSS: function () { return this.value; }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.ruleset = visitor.visit(this.ruleset); - }, - toCSS: function (env) { - var features = this.features.toCSS(env); - - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - toCSS: function () { return ""; }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval(new(tree.evalEnv)(env, - [this, frame].concat(mixinFrames))); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] - .concat(env.frames)))) { - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '-' + this.value.toCSS(env); - }, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { type: "Operation", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - toCSS: function (env) { - var separator = this.isSpaced ? " " : ""; - return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS(); - } -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - return '(' + this.value.toCSS(env).trim() + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function (env) { - if (this.variable) { return "" } - else { - try { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - } - }, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.index, this.currentFileInfo, this.inline); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - this.selectors = visitor.visit(this.selectors); - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - if (this.firstRoot && rule instanceof tree.Rule) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null}; - } - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - // Remove last semicolon - if (env.compress && rules.length) { - rule = rules[rules.length - 1]; - if (rule.charAt(rule.length - 1) === ';') { - rules[rules.length - 1] = rule.substring(0, rule.length - 1); - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = this.paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0), selector.extendList); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([], selector.extendList); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel, extendList; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements, extendList) { - this.elements = elements; - this.extendList = extendList || []; -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList) - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; - }, - eval: function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - })); - }, - toCSS: function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo }; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'strictImports', // option - - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'mime', // browser only - mime type for sheet import - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' = absolute path to the entry file - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - tree.parseEnv.prototype.toSheet = function (path) { - var env = new tree.parseEnv(this); - env.href = path; - //env.title = path; - env.type = this.mime; - return env; - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits' // whether units need to evaluate correctly - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - } -})(require('./tree'));(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - } - }; - -})(require('./tree'));(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode; - - if (!importNode.css) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && !evaldImportNode.css) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - if (imported && !importNode.options.multiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - } else { - subFinish(); - } - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree'));(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.ruleset.root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree'));(function (tree) { - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - } - } - - // now find every selector and apply the extends that apply to all extends - // and the ones which apply to an individual extend - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator == '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - return false; - }, - extendSelector:function (matches, selectorPath, replacementSelector) { - - //for a set of matches, replace each match with the replacement selector - - var currentSelectorPathIndex = 0, - currentSelectorPathElementIndex = 0, - path = [], - matchIndex, - selector, - firstElement, - match; - - for (matchIndex = 0; matchIndex < matches.length; matchIndex++) { - match = matches[matchIndex]; - selector = selectorPath[match.pathIndex]; - firstElement = new tree.Element( - match.initialCombinator, - replacementSelector.elements[0].value, - replacementSelector.elements[0].index - ); - - if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)) - )); - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selector.elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree'));// -// browser.js - client-side engine -// - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -var cache = null; - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); -var typePattern = /^text\/(x-)?less$/; - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -var session_cache = ''; -less.modifyVars = function(record) { - var str = session_cache; - for (var name in record) { - str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - new(less.Parser)(new less.tree.parseEnv(less)).parse(str, function (e, root) { - if (e) { - error(e, "session_cache"); - } else { - createCSS(root.toCSS(less), less.sheets[less.sheets.length - 1]); - } - }); -}; - -less.refresh = function (reload) { - var startTime, endTime; - startTime = endTime = new(Date); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache."); - } else { - log("parsed " + sheet.href + " successfully."); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); - (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); - endTime = new(Date); - }, reload); - - loadStyles(); -}; -less.refreshStyles = loadStyles; - -less.refresh(less.env === 'development'); - -function loadStyles() { - var styles = document.getElementsByTagName('style'); - for (var i = 0; i < styles.length; i++) { - if (styles[i].type.match(typePattern)) { - var env = new less.tree.parseEnv(less); - env.filename = document.location.href.replace(/#.*$/, ''); - - new(less.Parser)(env).parse(styles[i].innerHTML || '', function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - var style = styles[i]; - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }); - } - } -} - -function loadStyleSheets(callback, reload) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); - } -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(sheet.href, window.location.href); - var href = hrefParts.url; - var css = cache && cache.getItem(href); - var timestamp = cache && cache.getItem(href + ':timestamp'); - var styles = { css: css, timestamp: timestamp }; - var env; - var newFileInfo = { - relativeUrls: less.relativeUrls, - currentDirectory: hrefParts.path, - filename: href - }; - - if (sheet instanceof less.tree.parseEnv) { - env = new less.tree.parseEnv(sheet); - newFileInfo.entryPath = env.currentFileInfo.entryPath; - newFileInfo.rootpath = env.currentFileInfo.rootpath; - newFileInfo.rootFilename = env.currentFileInfo.rootFilename; - } else { - env = new less.tree.parseEnv(less); - env.mime = sheet.type; - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - } - - if (env.relativeUrls) { - //todo - this relies on option being set on less object rather than being passed in as an option - // - need an originalRootpath - if (less.rootpath) { - newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - xhr(href, sheet.type, function (data, lastModified) { - // Store data this session - session_cache += data.replace(/@import .+?;/ig, ''); - - if (!reload && styles && lastModified && - (new(Date)(lastModified).valueOf() === - new(Date)(styles.timestamp).valueOf())) { - // Use local copy - createCSS(styles.css, sheet); - callback(null, null, data, sheet, { local: true, remaining: remaining }, href); - } else { - // Use remote copy (re-parse) - try { - env.contents[href] = data; // Updating content cache - env.paths = [hrefParts.path]; - env.currentFileInfo = newFileInfo; - - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href); - //TODO - there must be a better way? A generic less-to-css function that can both call error - //and removeNode where appropriate - //should also add tests - if (env.currentFileInfo.rootFilename === href) { - removeNode(document.getElementById('less-error-message:' + extractId(href))); - } - } catch (e) { - callback(e, null, null, sheet); - } - }); - } catch (e) { - callback(e, null, null, sheet); - } - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, null, sheet); - }); -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss == null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl); - } - if (oldCss && keepOldCss === false) { - head.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save'); - } - } -} - -function xhr(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new(XMLHttpRequest); - } else { - try { - return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX."); - return null; - } - } -} - -function removeNode(node) { - return node && node.parentNode.removeChild(node); -} - -function log(str) { - if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } -} - -function error(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, error = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] != undefined) { - error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + error.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} -})(window); diff --git a/dist/less-1.4.2.min.js b/dist/less-1.4.2.min.js deleted file mode 100644 index e4a34ff25..000000000 --- a/dist/less-1.4.2.min.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - * LESS - Leaner CSS v1.4.2 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache 2.0 License. - * - * @licence - */(function(e,t){function n(t){return e.less[t.split("/")[1]]}function f(){r.env==="development"?(r.optimization=0,r.watchTimer=setInterval(function(){r.watchMode&&g(function(e,t,n,i,s){e?k(e,i.href):t&&S(t.toCSS(r),i,s.lastModified)})},r.poll)):r.optimization=3}function m(){var e=document.getElementsByTagName("style");for(var t=0;t0&&(s.splice(o-1,2),o-=2)}return i.hostPart=r[1],i.directories=s,i.path=r[1]+s.join("/"),i.fileUrl=i.path+(r[4]||""),i.url=i.fileUrl+(r[5]||""),i}function w(t,n,i,s){var o=b(t.href,e.location.href),u=o.url,a=l&&l.getItem(u),f=l&&l.getItem(u+":timestamp"),c={css:a,timestamp:f},h,p={relativeUrls:r.relativeUrls,currentDirectory:o.path,filename:u};t instanceof r.tree.parseEnv?(h=new r.tree.parseEnv(t),p.entryPath=h.currentFileInfo.entryPath,p.rootpath=h.currentFileInfo.rootpath,p.rootFilename=h.currentFileInfo.rootFilename):(h=new r.tree.parseEnv(r),h.mime=t.type,p.entryPath=o.path,p.rootpath=r.rootpath||o.path,p.rootFilename=u),h.relativeUrls&&(r.rootpath?p.rootpath=b(r.rootpath+y(o.path,p.entryPath)).path:p.rootpath=o.path),x(u,t.type,function(e,a){v+=e.replace(/@import .+?;/ig,"");if(!i&&c&&a&&(new Date(a)).valueOf()===(new Date(c.timestamp)).valueOf())S(c.css,t),n(null,null,e,t,{local:!0,remaining:s},u);else try{h.contents[u]=e,h.paths=[o.path],h.currentFileInfo=p,(new r.Parser(h)).parse(e,function(r,i){if(r)return n(r,null,null,t);try{n(r,i,e,t,{local:!1,lastModified:a,remaining:s},u),h.currentFileInfo.rootFilename===u&&N(document.getElementById("less-error-message:"+E(u)))}catch(r){n(r,null,null,t)}})}catch(f){n(f,null,null,t)}},function(e,r){n({type:"File",message:"'"+r+"' wasn't found ("+e+")"},null,null,t)})}function E(e){return e.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function S(e,t,n){var r=t.href||"",i="less:"+(t.title||E(r)),s=document.getElementById(i),o=!1,u=document.createElement("style");u.setAttribute("type","text/css"),t.media&&u.setAttribute("media",t.media),u.id=i;if(u.styleSheet)try{u.styleSheet.cssText=e}catch(a){throw new Error("Couldn't reassign styleSheet.cssText.")}else u.appendChild(document.createTextNode(e)),o=s!==null&&s.childNodes.length>0&&u.childNodes.length>0&&s.firstChild.nodeValue===u.firstChild.nodeValue;var f=document.getElementsByTagName("head")[0];if(s==null||o===!1){var c=t&&t.nextSibling||null;(c||document.getElementsByTagName("head")[0]).parentNode.insertBefore(u,c)}s&&o===!1&&f.removeChild(s);if(n&&l){C("saving "+r+" to cache.");try{l.setItem(r,e),l.setItem(r+":timestamp",n)}catch(a){C("failed to save")}}}function x(e,t,n,i){function a(t,n,r){t.status>=200&&t.status<300?n(t.responseText,t.getResponseHeader("Last-Modified")):typeof r=="function"&&r(t.status,e)}var s=T(),u=o?r.fileAsync:r.async;typeof s.overrideMimeType=="function"&&s.overrideMimeType("text/css"),s.open("GET",e,u),s.setRequestHeader("Accept",t||"text/x-less, text/css; q=0.9, */*; q=0.5"),s.send(null),o&&!r.fileAsync?s.status===0||s.status>=200&&s.status<300?n(s.responseText):i(s.status,e):u?s.onreadystatechange=function(){s.readyState==4&&a(s,n,i)}:a(s,n,i)}function T(){if(e.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(t){return C("browser doesn't support AJAX."),null}}function N(e){return e&&e.parentNode.removeChild(e)}function C(e){r.env=="development"&&typeof console!="undefined"&&console.log("less: "+e)}function k(e,n){var i="less-error-message:"+E(n||""),s='
  • {content}
  • ',o=document.createElement("div"),u,a,f=[],l=e.filename||n,c=l.match(/([^\/]+(\?.*)?)$/)[1];o.id=i,o.className="less-error-message",a="

    "+(e.type||"Syntax")+"Error: "+(e.message||"There is an error in your .less file")+"

    "+'

    in '+c+" ";var h=function(e,n,r){e.extract[n]!=t&&f.push(s.replace(/\{line\}/,(parseInt(e.line)||0)+(n-1)).replace(/\{class\}/,r).replace(/\{content\}/,e.extract[n]))};e.extract?(h(e,0,""),h(e,1,"line"),h(e,2,""),a+="on line "+e.line+", column "+(e.column+1)+":

    "+"
      "+f.join("")+"
    "):e.stack&&(a+="
    "+e.stack.split("\n").slice(1).join("
    ")),o.innerHTML=a,S([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),o.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),r.env=="development"&&(u=setInterval(function(){document.body&&(document.getElementById(i)?document.body.replaceChild(o,document.getElementById(i)):document.body.insertBefore(o,document.body.firstChild),clearInterval(u))},10))}var r,i,s;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof e=="undefined"?r={}:r=e.less={},i=r.tree={},r.mode="rhino"):typeof e=="undefined"?(r=exports,i=n("./tree"),r.mode="node"):(typeof e.less=="undefined"&&(e.less={}),r=e.less,i=e.less.tree={},r.mode="browser"),r.Parser=function(t){function m(){a=c[u],f=o,h=o}function g(){c[u]=a,o=f,h=o}function y(){o>h&&(c[u]=c[u].slice(o-h),h=o)}function b(e){var t=e.charCodeAt(0);return t===32||t===10||t===9}function w(e){var t,n,r,i,a;if(e instanceof Function)return e.call(p.parsers);if(typeof e=="string")t=s.charAt(o)===e?e:null,r=1,y();else{y();if(!(t=e.exec(c[u])))return null;r=t[0].length}if(t)return E(r),typeof t=="string"?t:t.length===1?t[0]:t}function E(e){var t=o,n=u,r=o+c[u].length,i=o+=e;while(o=0&&t.charAt(n)!=="\n";n--)r++;return{line:typeof e=="number"?(t.slice(0,e).match(/\n/g)||"").length:null,column:r}}function k(e,t,i){var s=i.currentFileInfo.filename;return r.mode!=="browser"&&r.mode!=="rhino"&&(s=n("path").resolve(s)),{lineNumber:C(e,t).line+1,fileName:s}}function L(e,t){var n=N(e,t),r=C(e.index,n),i=r.line,s=r.column,o=n.split("\n");this.type=e.type||"Syntax",this.message=e.message,this.filename=e.filename||t.currentFileInfo.filename,this.index=e.index,this.line=typeof i=="number"?i+1:null,this.callLine=e.call&&C(e.call,n).line+1,this.callExtract=o[C(e.call,n).line],this.stack=e.stack,this.column=s,this.extract=[o[i-1],o[i],o[i+1]]}var s,o,u,a,f,l,c,h,p,d=this;t instanceof i.parseEnv||(t=new i.parseEnv(t));var v=this.imports={paths:t.paths||[],queue:[],files:t.files,contents:t.contents,mime:t.mime,error:null,push:function(e,n,i){var s=this;this.queue.push(e),r.Parser.importer(e,n,function(t,n,r){s.queue.splice(s.queue.indexOf(e),1);var o=r in s.files;s.files[r]=n,t&&!s.error&&(s.error=t),i(t,n,o)},t)}};return L.prototype=new Error,L.prototype.constructor=L,this.env=t=t||{},this.optimization="optimization"in this.env?this.env.optimization:1,p={imports:v,parse:function(e,a){var f,d,v,m,g,y,b=[],E,S=null;o=u=h=l=0,s=e.replace(/\r\n/g,"\n"),s=s.replace(/^\uFEFF/,""),c=function(e){var n=0,r=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,i=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,o=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,u=0,a,f=e[0],l;for(var c=0,h,p;c0?"missing closing `}`":"missing opening `{`",filename:t.currentFileInfo.filename},t)),e.map(function(e){return e.join("")})}([[]]);if(S)return a(new L(S,t));try{f=new i.Ruleset([],w(this.parsers.primary)),f.root=!0,f.firstRoot=!0}catch(x){return a(new L(x,t))}f.toCSS=function(e){var s,o,u;return function(s,o){s=s||{};var u,a=new i.evalEnv(s);typeof o=="object"&&!Array.isArray(o)&&(o=Object.keys(o).map(function(e){var t=o[e];return t instanceof i.Value||(t instanceof i.Expression||(t=new i.Expression([t])),t=new i.Value([t])),new i.Rule("@"+e,t,!1,0)}),a.frames=[new i.Ruleset(null,o)]);try{var f=e.call(this,a);(new i.joinSelectorVisitor).run(f),(new i.processExtendsVisitor).run(f);var l=f.toCSS({compress:Boolean(s.compress),dumpLineNumbers:t.dumpLineNumbers,strictUnits:Boolean(s.strictUnits)})}catch(c){throw new L(c,t)}return s.yuicompress&&r.mode==="node"?n("ycssmin").cssmin(l,s.maxLineLen):s.compress?l.replace(/(\s)+/g,"$1"):l}}(f.eval);if(o=0&&s.charAt(T)!=="\n";T--)N++;S={type:"Parse",message:"Unrecognised input",index:o,filename:t.currentFileInfo.filename,line:g,column:N,extract:[y[g-2],y[g-1],y[g]]}}var C=function(e){e=S||e||p.imports.error,e?(e instanceof L||(e=new L(e,t)),a(e)):a(null,f)};t.processImports!==!1?(new i.importVisitor(this.imports,C)).run(f):C()},parsers:{primary:function(){var e,t=[];while((e=w(this.extendRule)||w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/)||w(/^;+/))e&&t.push(e);return t},comment:function(){var e;if(s.charAt(o)!=="/")return;if(s.charAt(o+1)==="/")return new i.Comment(w(/^\/\/.*/),!0);if(e=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new i.Comment(e)},entities:{quoted:function(){var e,n=o,r,u=o;s.charAt(n)==="~"&&(n++,r=!0);if(s.charAt(n)!=='"'&&s.charAt(n)!=="'")return;r&&w("~");if(e=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new i.Quoted(e[0],e[1]||e[2],r,u,t.currentFileInfo)},keyword:function(){var e;if(e=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return i.colors.hasOwnProperty(e)?new i.Color(i.colors[e].slice(1)):new i.Keyword(e)},call:function(){var e,n,r,s,a=o;if(!(e=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(c[u])))return;e=e[1],n=e.toLowerCase();if(n==="url")return null;o+=e.length;if(n==="alpha"){s=w(this.alpha);if(typeof s!="undefined")return s}w("("),r=w(this.entities.arguments);if(!w(")"))return;if(e)return new i.Call(e,r,a,t.currentFileInfo)},arguments:function(){var e=[],t;while(t=w(this.entities.assignment)||w(this.expression)){e.push(t);if(!w(","))break}return e},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)||w(this.entities.unicodeDescriptor)},assignment:function(){var e,t;if((e=w(/^\w+(?=\s?=)/i))&&w("=")&&(t=w(this.entity)))return new i.Assignment(e,t)},url:function(){var e;if(s.charAt(o)!=="u"||!w(/^url\(/))return;return e=w(this.entities.quoted)||w(this.entities.variable)||w(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",S(")"),new i.URL(e.value!=null||e instanceof i.Variable?e:new i.Anonymous(e),t.currentFileInfo)},variable:function(){var e,n=o;if(s.charAt(o)==="@"&&(e=w(/^@@?[\w-]+/)))return new i.Variable(e,n,t.currentFileInfo)},variableCurly:function(){var e,n,r=o;if(s.charAt(o)==="@"&&(n=w(/^@\{([\w-]+)\}/)))return new i.Variable("@"+n[1],r,t.currentFileInfo)},color:function(){var e;if(s.charAt(o)==="#"&&(e=w(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/)))return new i.Color(e[1])},dimension:function(){var e,t=s.charCodeAt(o);if(t>57||t<43||t===47||t==44)return;if(e=w(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))return new i.Dimension(e[1],e[2])},unicodeDescriptor:function(){var e;if(e=w(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))return new i.UnicodeDescriptor(e[0])},javascript:function(){var e,t=o,n;s.charAt(t)==="~"&&(t++,n=!0);if(s.charAt(t)!=="`")return;n&&w("~");if(e=w(/^`([^`]*)`/))return new i.JavaScript(e[1],o,n)}},variable:function(){var e;if(s.charAt(o)==="@"&&(e=w(/^(@[\w-]+)\s*:/)))return e[1]},extend:function(e){var t,n,r=o,s,u=[];if(!w(e?/^&:extend\(/:/^:extend\(/))return;do{s=null,t=[];for(;;){s=w(/^(all)(?=\s*(\)|,))/);if(s)break;n=w(this.element);if(!n)break;t.push(n)}s=s&&s[1],u.push(new i.Extend(new i.Selector(t),s,r))}while(w(","));return S(/^\)/),e&&S(/^;/),u},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var e=[],n,r,u,a,f,l=o,c=s.charAt(o),h=!1;if(c!=="."&&c!=="#")return;m();while(n=w(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/))e.push(new i.Element(r,n,o)),r=w(">");w("(")&&(u=this.mixin.args.call(this,!0).args,S(")")),u=u||[],w(this.important)&&(h=!0);if(e.length>0&&(w(";")||T("}")))return new i.mixin.Call(e,u,l,t.currentFileInfo,h);g()},args:function(e){var t=[],n=[],r,u=[],a,f,l,c,h,p={args:null,variadic:!1};for(;;){if(e)h=w(this.expression);else{w(this.comment);if(s.charAt(o)==="."&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({variadic:!0});break}h=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)}if(!h)break;l=null,h.throwAwayComments&&h.throwAwayComments(),c=h;var d=null;if(e){if(h.value.length==1)var d=h.value[0]}else d=h;if(d&&d instanceof i.Variable)if(w(":"))t.length>0&&(r&&x("Cannot mix ; and , as delimiter types"),a=!0),c=S(this.expression),l=f=d.name;else{if(!e&&w(/^\.{3}/)){p.variadic=!0,w(";")&&!r&&(r=!0),(r?n:u).push({name:h.name,variadic:!0});break}e||(f=l=d.name,c=null)}c&&t.push(c),u.push({name:l,value:c});if(w(","))continue;if(w(";")||r)a&&x("Cannot mix ; and , as delimiter types"),r=!0,t.length>1&&(c=new i.Value(t)),n.push({name:f,value:c}),f=null,t=[],a=!1}return p.args=r?n:u,p},definition:function(){var e,t=[],n,r,u,a,f,c=!1;if(s.charAt(o)!=="."&&s.charAt(o)!=="#"||T(/^[^{]*\}/))return;m();if(n=w(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){e=n[1];var h=this.mixin.args.call(this,!1);t=h.args,c=h.variadic,w(")")||(l=o,g()),w(this.comment),w(/^when/)&&(f=S(this.conditions,"expected condition")),r=w(this.block);if(r)return new i.mixin.Definition(e,t,r,f,c);g()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||T("}")},alpha:function(){var e;if(!w(/^\(opacity=/i))return;if(e=w(/^\d+/)||w(this.entities.variable))return S(")"),new i.Alpha(e)},element:function(){var e,t,n,r;n=w(this.combinator),e=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||w("*")||w("&")||w(this.attribute)||w(/^\([^()@]+\)/)||w(/^[\.#](?=@)/)||w(this.entities.variableCurly),e||w("(")&&(r=w(this.selector))&&w(")")&&(e=new i.Paren(r));if(e)return new i.Element(n,e,o)},combinator:function(){var e=s.charAt(o);if(e===">"||e==="+"||e==="~"||e==="|"){o++;while(s.charAt(o).match(/\s/))o++;return new i.Combinator(e)}return s.charAt(o-1).match(/\s/)?new i.Combinator(" "):new i.Combinator(null)},selector:function(){var e,t,n=[],r,u,a=[];while((u=w(this.extend))||(t=w(this.element))){u?a.push.apply(a,u):(a.length&&x("Extend can only be used at the end of selector"),r=s.charAt(o),n.push(t),t=null);if(r==="{"||r==="}"||r===";"||r===","||r===")")break}if(n.length>0)return new i.Selector(n,a);a.length&&x("Extend must be used to extend a selector, it cannot be used on its own")},attribute:function(){var e="",t,n,r;if(!w("["))return;(t=w(this.entities.variableCurly))||(t=S(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/));if(r=w(/^[|~*$^]?=/))n=w(this.entities.quoted)||w(/^[\w-]+/)||w(this.entities.variableCurly);return S("]"),new i.Attribute(t,r,n)},block:function(){var e;if(w("{")&&(e=w(this.primary))&&w("}"))return e},ruleset:function(){var e=[],n,r,u;m(),t.dumpLineNumbers&&(u=k(o,s,t));while(n=w(this.selector)){e.push(n),w(this.comment);if(!w(","))break;w(this.comment)}if(e.length>0&&(r=w(this.block))){var a=new i.Ruleset(e,r,t.strictImports);return t.dumpLineNumbers&&(a.debugInfo=u),a}l=o,g()},rule:function(e){var n,r,u=s.charAt(o),a;m();if(u==="."||u==="#"||u==="&")return;if(n=w(this.variable)||w(this.property)){r=!e&&(t.compress||n.charAt(0)==="@")?w(this.value)||w(this.anonymousValue):w(this.anonymousValue)||w(this.value),a=w(this.important);if(r&&w(this.end))return new i.Rule(n,r,a,f,t.currentFileInfo);l=o,g();if(r&&!e)return this.rule(!0)}},anonymousValue:function(){var e;if(e=/^([^@+\/'"*`(;{}-]*);/.exec(c[u]))return o+=e[0].length-1,new i.Anonymous(e[1])},"import":function(){var e,n,r=o;m();var s=w(/^@import?\s+/),u=(s?w(this.importOptions):null)||{};if(s&&(e=w(this.entities.quoted)||w(this.entities.url))){n=w(this.mediaFeatures);if(w(";"))return n=n&&new i.Value(n),new i.Import(e,n,u,r,t.currentFileInfo)}g()},importOptions:function(){var e,t={},n,r;if(!w("("))return null;do if(e=w(this.importOption)){n=e,r=!0;switch(n){case"css":n="less",r=!1;break;case"once":n="multiple",r=!1}t[n]=r;if(!w(","))break}while(e);return S(")"),t},importOption:function(){var e=w(/^(less|css|multiple|once)/);if(e)return e[1]},mediaFeature:function(){var e,n,r=[];do if(e=w(this.entities.keyword))r.push(e);else if(w("(")){n=w(this.property),e=w(this.value);if(!w(")"))return null;if(n&&e)r.push(new i.Paren(new i.Rule(n,e,null,o,t.currentFileInfo,!0)));else{if(!e)return null;r.push(new i.Paren(e))}}while(e);if(r.length>0)return new i.Expression(r)},mediaFeatures:function(){var e,t=[];do if(e=w(this.mediaFeature)){t.push(e);if(!w(","))break}else if(e=w(this.entities.variable)){t.push(e);if(!w(","))break}while(e);return t.length>0?t:null},media:function(){var e,n,r,u;t.dumpLineNumbers&&(u=k(o,s,t));if(w(/^@media/)){e=w(this.mediaFeatures);if(n=w(this.block))return r=new i.Media(n,e),t.dumpLineNumbers&&(r.debugInfo=u),r}},directive:function(){var e,n,r,u,a,f,l,c,h,p;if(s.charAt(o)!=="@")return;if(n=w(this["import"])||w(this.media))return n;m(),e=w(/^@[a-z-]+/);if(!e)return;l=e,e.charAt(1)=="-"&&e.indexOf("-",2)>0&&(l="@"+e.slice(e.indexOf("-",2)+1));switch(l){case"@font-face":c=!0;break;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":c=!0;break;case"@page":case"@document":case"@supports":case"@keyframes":c=!0,h=!0;break;case"@namespace":p=!0}h&&(e+=" "+(w(/^[^{]+/)||"").trim());if(c){if(r=w(this.block))return new i.Directive(e,r)}else if((n=p?w(this.expression):w(this.entity))&&w(";")){var d=new i.Directive(e,n);return t.dumpLineNumbers&&(d.debugInfo=k(o,s,t)),d}g()},value:function(){var e,t=[],n;while(e=w(this.expression)){t.push(e);if(!w(","))break}if(t.length>0)return new i.Value(t)},important:function(){if(s.charAt(o)==="!")return w(/^! *important/)},sub:function(){var e,t;if(w("("))if(e=w(this.addition))return t=new i.Expression([e]),S(")"),t.parens=!0,t},multiplication:function(){var e,t,n,r,u,a=[];if(e=w(this.operand)){u=b(s.charAt(o-1));while(!T(/^\/[*\/]/)&&(n=w("/")||w("*"))){if(!(t=w(this.operand)))break;e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1))}return r||e}},addition:function(){var e,t,n,r,u;if(e=w(this.multiplication)){u=b(s.charAt(o-1));while((n=w(/^[-+]\s+/)||!u&&(w("+")||w("-")))&&(t=w(this.multiplication)))e.parensInOp=!0,t.parensInOp=!0,r=new i.Operation(n,[r||e,t],u),u=b(s.charAt(o-1));return r||e}},conditions:function(){var e,t,n=o,r;if(e=w(this.condition)){while(w(",")&&(t=w(this.condition)))r=new i.Condition("or",r||e,t,n);return r||e}},condition:function(){var e,t,n,r,s=o,u=!1;w(/^not/)&&(u=!0),S("(");if(e=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(r=w(/^(?:>=|=<|[<=>])/))?(t=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?n=new i.Condition(r,e,t,s,u):x("expected expression"):n=new i.Condition("=",e,new i.Keyword("true"),s,u),S(")"),w(/^and/)?new i.Condition("and",n,w(this.condition)):n},operand:function(){var e,t=s.charAt(o+1);s.charAt(o)==="-"&&(t==="@"||t==="(")&&(e=w("-"));var n=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return e&&(n.parensInOp=!0,n=new i.Negative(n)),n},expression:function(){var e,t,n=[],r;while(e=w(this.addition)||w(this.entity))n.push(e),!T(/^\/[\/*]/)&&(t=w("/"))&&n.push(new i.Anonymous(t));if(n.length>0)return new i.Expression(n)},property:function(){var e;if(e=w(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/))return e[1]}}}};if(r.mode==="browser"||r.mode==="rhino")r.Parser.importer=function(e,t,n,r){!/^([a-z-]+:)?\//.test(e)&&t.currentDirectory&&(e=t.currentDirectory+e);var i=r.toSheet(e);i.processImports=!1,i.currentFileInfo=t,w(i,function(e,t,r,i,s,o){n.call(null,e,t,o)},!0)};(function(r){function u(e){return r.functions.hsla(e.h,e.s,e.l,e.a)}function a(e,t){return e instanceof r.Dimension&&e.unit.is("%")?parseFloat(e.value*t/100):f(e)}function f(e){if(e instanceof r.Dimension)return parseFloat(e.unit.is("%")?e.value/100:e.value);if(typeof e=="number")return e;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function l(e){return Math.min(1,Math.max(0,e))}r.functions={rgb:function(e,t,n){return this.rgba(e,t,n,1)},rgba:function(e,t,n,i){var s=[e,t,n].map(function(e){return a(e,256)});return i=f(i),new r.Color(s,i)},hsl:function(e,t,n){return this.hsla(e,t,n,1)},hsla:function(e,t,n,r){function o(e){return e=e<0?e+1:e>1?e-1:e,e*6<1?s+(i-s)*e*6:e*2<1?i:e*3<2?s+(i-s)*(2/3-e)*6:s}e=f(e)%360/360,t=l(f(t)),n=l(f(n)),r=l(f(r));var i=n<=.5?n*(t+1):n+t-n*t,s=n*2-i;return this.rgba(o(e+1/3)*255,o(e)*255,o(e-1/3)*255,r)},hsv:function(e,t,n){return this.hsva(e,t,n,1)},hsva:function(e,t,n,r){e=f(e)%360/360*360,t=f(t),n=f(n),r=f(r);var i,s;i=Math.floor(e/60%6),s=e/60-i;var o=[n,n*(1-t),n*(1-s*t),n*(1-(1-s)*t)],u=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(o[u[i][0]]*255,o[u[i][1]]*255,o[u[i][2]]*255,r)},hue:function(e){return new r.Dimension(Math.round(e.toHSL().h))},saturation:function(e){return new r.Dimension(Math.round(e.toHSL().s*100),"%")},lightness:function(e){return new r.Dimension(Math.round(e.toHSL().l*100),"%")},hsvhue:function(e){return new r.Dimension(Math.round(e.toHSV().h))},hsvsaturation:function(e){return new r.Dimension(Math.round(e.toHSV().s*100),"%")},hsvvalue:function(e){return new r.Dimension(Math.round(e.toHSV().v*100),"%")},red:function(e){return new r.Dimension(e.rgb[0])},green:function(e){return new r.Dimension(e.rgb[1])},blue:function(e){return new r.Dimension(e.rgb[2])},alpha:function(e){return new r.Dimension(e.toHSL().a)},luma:function(e){return new r.Dimension(Math.round(e.luma()*e.alpha*100),"%")},saturate:function(e,t){var n=e.toHSL();return n.s+=t.value/100,n.s=l(n.s),u(n)},desaturate:function(e,t){var n=e.toHSL();return n.s-=t.value/100,n.s=l(n.s),u(n)},lighten:function(e,t){var n=e.toHSL();return n.l+=t.value/100,n.l=l(n.l),u(n)},darken:function(e,t){var n=e.toHSL();return n.l-=t.value/100,n.l=l(n.l),u(n)},fadein:function(e,t){var n=e.toHSL();return n.a+=t.value/100,n.a=l(n.a),u(n)},fadeout:function(e,t){var n=e.toHSL();return n.a-=t.value/100,n.a=l(n.a),u(n)},fade:function(e,t){var n=e.toHSL();return n.a=t.value/100,n.a=l(n.a),u(n)},spin:function(e,t){var n=e.toHSL(),r=(n.h+t.value)%360;return n.h=r<0?360+r:r,u(n)},mix:function(e,t,n){n||(n=new r.Dimension(50));var i=n.value/100,s=i*2-1,o=e.toHSL().a-t.toHSL().a,u=((s*o==-1?s:(s+o)/(1+s*o))+1)/2,a=1-u,f=[e.rgb[0]*u+t.rgb[0]*a,e.rgb[1]*u+t.rgb[1]*a,e.rgb[2]*u+t.rgb[2]*a],l=e.alpha*i+t.alpha*(1-i);return new r.Color(f,l)},greyscale:function(e){return this.desaturate(e,new r.Dimension(100))},contrast:function(e,t,n,r){if(!e.rgb)return null;typeof n=="undefined"&&(n=this.rgba(255,255,255,1)),typeof t=="undefined"&&(t=this.rgba(0,0,0,1));if(t.luma()>n.luma()){var i=n;n=t,t=i}return typeof r=="undefined"?r=.43:r=f(r),e.luma()*e.alpha=d){if(this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",o,v,d),(new r.URL(i||t,this.currentFileInfo)).eval(this.env);this.env.silent||console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!",o,v,d)}p=f?p.toString("base64"):encodeURIComponent(p);var m="'data:"+s+","+p+"'";return new r.URL(new r.Anonymous(m))}},r._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(e){var i=n("path").extname(e),s=r._mime._types[i];if(s===t)throw new Error('Optional dependency "mime" is required for '+i);return s},charsets:{lookup:function(e){return e&&/^text\//.test(e)?"UTF-8":""}}};var i=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],s=function(e,t){return function(n){return t!=null&&(n=n.unify()),this._math(Math[e],t,n)}};for(var o=0;o255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("");return n&&(r=r.split(""),r[0]==r[1]&&r[2]==r[3]&&r[4]==r[5]?r=r[0]+r[2]+r[4]:r=r.join("")),"#"+r},operate:function(t,n,r){var i=[];r instanceof e.Color||(r=r.toColor());for(var s=0;s<3;s++)i[s]=e.operate(t,n,this.rgb[s],r.rgb[s]);return new e.Color(i,this.alpha+r.alpha)},toHSL:function(){var e=this.rgb[0]/255,t=this.rgb[1]/255,n=this.rgb[2]/255,r=this.alpha,i=Math.max(e,t,n),s=Math.min(e,t,n),o,u,a=(i+s)/2,f=i-s;if(i===s)o=u=0;else{u=a>.5?f/(2-i-s):f/(i+s);switch(i){case e:o=(t-n)/f+(t255?255:e<0?0:e).toString(16),e.length===1?"0"+e:e}).join("")},compare:function(e){return e.rgb?e.rgb[0]===this.rgb[0]&&e.rgb[1]===this.rgb[1]&&e.rgb[2]===this.rgb[2]&&e.alpha===this.alpha?0:-1:-1}}}(n("../tree")),function(e){e.Comment=function(e,t){this.value=e,this.silent=!!t},e.Comment.prototype={type:"Comment",toCSS:function(e){return e.compress?"":this.value},eval:function(){return this}}}(n("../tree")),function(e){e.Condition=function(e,t,n,r,i){this.op=e.trim(),this.lvalue=t,this.rvalue=n,this.index=r,this.negate=i},e.Condition.prototype={type:"Condition",accept:function(e){this.lvalue=e.visit(this.lvalue),this.rvalue=e.visit(this.rvalue)},eval:function(e){var t=this.lvalue.eval(e),n=this.rvalue.eval(e),r=this.index,i,i=function(e){switch(e){case"and":return t&&n;case"or":return t||n;default:if(t.compare)i=t.compare(n);else{if(!n.compare)throw{type:"Type",message:"Unable to perform comparison",index:r};i=n.compare(t)}switch(i){case-1:return e==="<"||e==="=<";case 0:return e==="="||e===">="||e==="=<";case 1:return e===">"||e===">="}}}(this.op);return this.negate?!i:i}}}(n("../tree")),function(e){e.Dimension=function(n,r){this.value=parseFloat(n),this.unit=r&&r instanceof e.Unit?r:new e.Unit(r?[r]:t)},e.Dimension.prototype={type:"Dimension",accept:function(e){this.unit=e.visit(this.unit)},eval:function(e){return this},toColor:function(){return new e.Color([this.value,this.value,this.value])},toCSS:function(e){if(e&&e.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var t=this.value,n=String(t);t!==0&&t<1e-6&&t>-0.000001&&(n=t.toFixed(20).replace(/0+$/,""));if(e&&e.compress){if(t===0&&!this.unit.isAngle())return n;t>0&&t<1&&(n=n.substr(1))}return n+this.unit.toCSS(e)},operate:function(t,n,r){var i=e.operate(t,n,this.value,r.value),s=this.unit.clone();if(n==="+"||n==="-"){if(s.numerator.length===0&&s.denominator.length===0)s.numerator=r.unit.numerator.slice(0),s.denominator=r.unit.denominator.slice(0);else if(r.unit.numerator.length!=0||s.denominator.length!=0){r=r.convertTo(this.unit.usedUnits());if(t.strictUnits&&r.unit.toString()!==s.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+s.toString()+"' and '"+r.unit.toString()+"'.");i=e.operate(t,n,this.value,r.value)}}else n==="*"?(s.numerator=s.numerator.concat(r.unit.numerator).sort(),s.denominator=s.denominator.concat(r.unit.denominator).sort(),s.cancel()):n==="/"&&(s.numerator=s.numerator.concat(r.unit.denominator).sort(),s.denominator=s.denominator.concat(r.unit.numerator).sort(),s.cancel());return new e.Dimension(i,s)},compare:function(t){if(t instanceof e.Dimension){var n=this.unify(),r=t.unify(),i=n.value,s=r.value;return s>i?-1:s=1?this.numerator[0]:this.denominator.length>=1?this.denominator[0]:(!e||!e.strictUnits)&&this.backupUnit?this.backupUnit:""},toString:function(){var e,t=this.numerator.join("*");for(e=0;e0)for(n=0;n":e.compress?">":" > ","|":e.compress?"|":" | "}[this.value]}}}(n("../tree")),function(e){e.Expression=function(e){this.value=e},e.Expression.prototype={type:"Expression",accept:function(e){this.value=e.visit(this.value)},eval:function(t){var n,r=this.parens&&!this.parensInOp,i=!1;return r&&t.inParenthesis(),this.value.length>1?n=new e.Expression(this.value.map(function(e){return e.eval(t)})):this.value.length===1?(this.value[0].parens&&!this.value[0].parensInOp&&(i=!0),n=this.value[0].eval(t)):n=this,r&&t.outOfParenthesis(),this.parens&&this.parensInOp&&!t.isMathOn()&&!i&&(n=new e.Paren(n)),n},toCSS:function(e){return this.value.map(function(t){return t.toCSS?t.toCSS(e):""}).join(" ")},throwAwayComments:function(){this.value=this.value.filter(function(t){return!(t instanceof e.Comment)})}}}(n("../tree")),function(e){e.Extend=function(t,n,r){this.selector=t,this.option=n,this.index=r;switch(n){case"all":this.allowBefore=!0,this.allowAfter=!0;break;default:this.allowBefore=!1,this.allowAfter=!1}},e.Extend.prototype={type:"Extend",accept:function(e){this.selector=e.visit(this.selector)},eval:function(t){return new e.Extend(this.selector.eval(t),this.option,this.index)},clone:function(t){return new e.Extend(this.selector,this.option,this.index)},findSelfSelectors:function(e){var t=[],n;for(n=0;n1){var r=this.emptySelectors();n=new e.Ruleset(r,t.mediaBlocks),n.multiMedia=!0}return delete t.mediaBlocks,delete t.mediaPath,n},evalNested:function(t){var n,r,i=t.mediaPath.concat([this]);for(n=0;n0;n--)t.splice(n,0,new e.Anonymous("and"));return new e.Expression(t)})),new e.Ruleset([],[])},permute:function(e){if(e.length===0)return[];if(e.length===1)return e[0];var t=[],n=this.permute(e.slice(1));for(var r=0;r0){c=!0;for(a=0;athis.params.length)return!1;if(this.required>0&&n>this.params.length)return!1}r=Math.min(n,this.arity);for(var s=0;si.selectors[o].elements.length?Array.prototype.push.apply(r,i.find(new e.Selector(t.elements.slice(1)),n)):r.push(i);break}}),this._lookups[o]=r)},toCSS:function(t){var n=[],r=[],i=[],s=[],o,u,a;for(var f=0;f0){u=e.debugInfo(t,this),o=this.paths.map(function(e){return e.map(function(e){return e.toCSS(t)}).join("").trim()}).join(t.compress?",":",\n");for(var f=r.length-1;f>=0;f--)(r[f].slice(0,2)==="/*"||i.indexOf(r[f])===-1)&&i.unshift(r[f]);r=i,n.push(u+o+(t.compress?"{":" {\n ")+r.join(t.compress?"":"\n ")+(t.compress?"}":"\n}\n"))}return n.push(s),n.join("")+(t.compress?"\n":"")},joinSelectors:function(e,t,n){for(var r=0;r0)for(i=0;i0&&this.mergeElementsOnToSelectors(g,a);for(s=0;s0&&(l[0].elements=l[0].elements.slice(0),l[0].elements.push(new e.Element(f.combinator,"",0))),y.push(l);else for(o=0;o0?(h=l.slice(0),m=h.pop(),d=new e.Selector(m.elements.slice(0),r.extendList),v=!1):d=new e.Selector([],r.extendList),c.length>1&&(p=p.concat(c.slice(1))),c.length>0&&(v=!1,d.elements.push(new e.Element(f.combinator,c[0].elements[0].value,0)),d.elements=d.elements.concat(c[0].elements.slice(1))),v||h.push(d),h=h.concat(p),y.push(h)}a=y,g=[]}}g.length>0&&this.mergeElementsOnToSelectors(g,a);for(i=0;i0&&t.push(a[i])},mergeElementsOnToSelectors:function(t,n){var r,i,s;if(n.length==0){n.push([new e.Selector(t)]);return}for(r=0;r0?i[i.length-1]=new e.Selector(i[i.length-1].elements.concat(t),i[i.length-1].extendList):i.push(new e.Selector(t))}}}(n("../tree")),function(e){e.Selector=function(e,t){this.elements=e,this.extendList=t||[]},e.Selector.prototype={type:"Selector",accept:function(e){this.elements=e.visit(this.elements),this.extendList=e.visit(this.extendList)},match:function(e){var t=this.elements,n=t.length,r,i,s,o;r=e.elements.slice(e.elements.length&&e.elements[0].value==="&"?1:0),i=r.length,s=Math.min(n,i);if(i===0||n1?"["+e.value.map(function(e){return e.toCSS(!1)}).join(", ")+"]":e.toCSS(!1)}}(n("./tree")),function(e){var t=["paths","optimization","files","contents","relativeUrls","strictImports","dumpLineNumbers","compress","processImports","syncImport","mime","currentFileInfo"];e.parseEnv=function(e){r(e,this,t),this.contents||(this.contents={}),this.files||(this.files={});if(!this.currentFileInfo){var n=e&&e.filename||"input",i=n.replace(/[^\/\\]*$/,"");e&&(e.filename=null),this.currentFileInfo={filename:n,relativeUrls:this.relativeUrls,rootpath:e&&e.rootpath||"",currentDirectory:i,entryPath:i,rootFilename:n}}},e.parseEnv.prototype.toSheet=function(t){var n=new e.parseEnv(this);return n.href=t,n.type=this.mime,n};var n=["silent","verbose","compress","yuicompress","ieCompat","strictMath","strictUnits"];e.evalEnv=function(e,t){r(e,this,n),this.frames=t||[]},e.evalEnv.prototype.inParenthesis=function(){this.parensStack||(this.parensStack=[]),this.parensStack.push(!0)},e.evalEnv.prototype.outOfParenthesis=function(){this.parensStack.pop()},e.evalEnv.prototype.isMathOn=function(){return this.strictMath?this.parensStack&&this.parensStack.length:!0},e.evalEnv.prototype.isPathRelative=function(e){return!/^(?:[a-z-]+:|\/)/.test(e)};var r=function(e,t,n){if(!e)return;for(var r=0;r100){var d="{unable to calculate}",v="{unable to calculate}";try{d=u[0].selfSelectors[0].toCSS(),v=u[0].selector.toCSS()}catch(m){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+d+":extend("+v+")"}}return u.concat(f.doExtendChaining(u,n,r+1))}return u},inInheritanceChain:function(e,t){if(e===t)return!0;if(t.parents){if(this.inInheritanceChain(e,t.parents[0]))return!0;if(this.inInheritanceChain(e,t.parents[1]))return!0}return!1},visitRule:function(e,t){t.visitDeeper=!1},visitMixinDefinition:function(e,t){t.visitDeeper=!1},visitSelector:function(e,t){t.visitDeeper=!1},visitRuleset:function(e,t){if(e.root)return;var n,r,i,s=this.allExtendsStack[this.allExtendsStack.length-1],o=[],u=this,a;for(i=0;i0&&f[c.matched].combinator.value!==o?c=null:c.matched++,c&&(c.finished=c.matched===f.length,c.finished&&!e.allowAfter&&(i+1i&&s>0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,l.pathIndex)),o.push(new e.Selector(a.elements.slice(s,l.index).concat([f]).concat(r.elements.slice(1)))),i=l.endPathIndex,s=l.endPathElementIndex,s>=a.elements.length&&(s=0,i++);return i0&&(o[o.length-1].elements=o[o.length-1].elements.concat(n[i].elements.slice(s)),s=0,i++),o=o.concat(n.slice(i,n.length)),o},visitRulesetOut:function(e){},visitMedia:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitMediaOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(e,t){var n=e.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);n=n.concat(this.doExtendChaining(n,e.allExtends)),this.allExtendsStack.push(n)},visitDirectiveOut:function(e){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(n("./tree"));var o=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);r.env=r.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||o?"development":"production"),r.async=r.async||!1,r.fileAsync=r.fileAsync||!1,r.poll=r.poll||(o?1e3:1500);if(r.functions)for(var u in r.functions)r.tree.functions[u]=r.functions[u];var a=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);a&&(r.dumpLineNumbers=a[1]),r.watch=function(){return r.watchMode||(r.env="development",f()),this.watchMode=!0},r.unwatch=function(){return clearInterval(r.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&r.watch();var l=null;if(r.env!="development")try{l=typeof e.localStorage=="undefined"?null:e.localStorage}catch(c){}var h=document.getElementsByTagName("link"),p=/^text\/(x-)?less$/;r.sheets=[];for(var d=0;d - * Licensed under the Apache v2 License. - * - * @licence - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = chunks[j], memo = i, current = i; } - function restore() { chunks[j] = temp, i = memo, current = i; } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, length; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break; } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(chunks[j]); - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, line, lines, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - parser.imports.contents[env.currentFileInfo.filename] = input; - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': - if (!inParam) { - level++; - chunk.push(c); - break; - } - /* falls through */ - case '}': - if (!inParam) { - level--; - chunk.push(c); - chunks[++j] = chunk = []; - break; - } - /* falls through */ - case '(': - if (!inParam) { - inParam = true; - chunk.push(c); - break; - } - /* falls through */ - case ')': - if (inParam) { - inParam = false; - chunk.push(c); - break; - } - /* falls through */ - default: - chunk.push(c); - } - - i++; - } - if (level !== 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join(''); }); - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - evaldRoot = evaluate.call(this, evalEnv); - - new(tree.joinSelectorVisitor)() - .run(evaldRoot); - - new(tree.processExtendsVisitor)() - .run(evaldRoot); - - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - .run(evaldRoot); - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - return require('clean-css').process(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true, i, env.currentFileInfo); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(comment = $(this.comment)) { - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null; } - else { i += name.length; } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { - break; - } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) { - return; - } - - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - /*jshint eqnull:true */ - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $('~'); } - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i, env.currentFileInfo)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comments); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comments); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) { return; } - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++; } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var e, elements = [], c, extend, extendList = [], when, condition; - - while ((isLess && (extend = $(this.extend))) || (isLess && (when = $(/^when/))) || (e = $(this.element))) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e); - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var key, val, op; - - if (! $('[')) { return; } - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[0-9]+%/) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (s = $(this.lessSelector)) { - selectors.push(s); - $(this.comments); - if (! $(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - $(this.comments); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - if (name = $(this.variable) || $(this.ruleProperty)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - - important = $(this.important); - if (name[name.length-1] === "+") { - merge = true; - name = name.substr(0, name.length - 1); - } - - if (value && $(this.end)) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break; } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = ($(this.entities.keyword) || $(this.entities.variable))) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break; } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break; } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules, i, env.currentFileInfo); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break; } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while (peek(/^,\s*(not\s*)?\(/) && $(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true; } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|<=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-'); } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = []; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/)) { - return name[1]; - } - }, - ruleProperty: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*(\+?)\s*:/)) { - return name[1] + (name[2] || ""); - } - } - } - }; -}; - - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - /*jshint eqnull:true */ - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - // only works in node, needs interface to what is supported in environment - try { - returner = new Buffer(returner).toString('base64'); - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - /*jshint eqnull:true */ - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - output.add((env.compress ? '{' : ' {\n')); - env.tabLevel = (env.tabLevel || 0) + 1; - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); - for(var i = 0; i < rules.length; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - output.add(env.compress ? '' : '\n'); - } - env.tabLevel--; - output.add(tabSetStr + "}"); -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { return this; }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - /*jshint eqnull:true */ - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - - // 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 (this.alpha < 1.0) { - if (this.alpha === 0 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toRGB: function () { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)([], value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.rules = visitor.visit(this.rules); - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - if (!this.options.inline) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.rules = visitor.visit(this.rules); - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); - return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - //if (this.important) { - // isImportant = env.isImportant; - // env.isImportant = true; - //} - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - //if (this.important) { - // env.isImportant = isImportant; - //} - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { type: "Operation", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - for(var i = 0; i < this.paths.length; i++) { - this.paths[i] = visitor.visit(this.paths[i]); - } - } else { - this.selectors = visitor.visit(this.selectors); - } - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env); }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - var rule; - var i; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables; } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - debugInfo, // Line number debugging - rule, - firstRuleset = true, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - for(i = 0; i < this.paths.length; i++) { - path = this.paths[i]; - env.firstSelector = true; - for(j = 0; j < path.length; j++) { - path[j].genCSS(env, output); - env.firstSelector = false; - } - if (i + 1 < this.paths.length) { - output.add(env.compress ? ',' : (',\n' + tabSetStr)); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - for (i = 0; i < rulesetNodes.length; i++) { - if (ruleNodes.length && firstRuleset) { - output.add((env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr)); - } - if (!firstRuleset) { - output.add((env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr)); - } - firstRuleset = false; - rulesetNodes[i].genCSS(env, output); - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList || []; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList); - this.condition = visitor.visit(this.condition); - }, - createDerived: function(elements, extendList, evaldCondition) { - /*jshint eqnull:true */ - evaldCondition = evaldCondition != null ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env); - - return this.createDerived(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - }), evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple'// whether we are currently importing multiple copies - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - evald = this.flatten(evald); - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - }, - doAccept: function (node) { - node.accept(this); - }, - flatten: function(arr, master) { - return arr.reduce(this.flattenReduce.bind(this), master || []); - }, - flattenReduce: function(sum, element) { - if (element instanceof Array) { - sum = this.flatten(element, sum); - } else { - sum.push(element); - } - return sum; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.selectors = rulesetNode.selectors.filter(function(selector) { return selector.getIsOutput(); }); - if (rulesetNode.selectors.length === 0) { - rulesetNode.rules.length = 0; - } - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - return false; - } - }); - - // Compile rules and rulesets - for (var i = 0; i < rulesetNode.rules.length; i++) { - rule = rulesetNode.rules[i]; - - if (rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - rulesetNode.rules.splice(i, 1); - i--; - continue; - } - } - // accept the visitor to remove rules and refactor itself - // then we can decide now whether we want it or not - if (rulesetNode.rules.length > 0) { - rulesetNode.accept(this._visitor); - } - visitArgs.visitDeeper = false; - - this._mergeRules(rulesetNode.rules); - this._removeDuplicateRules(rulesetNode.rules); - - // now decide whether we keep the ruleset - if (rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || rulesetNode.rules.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - parts = groups[key] = []; - } else { - rules.splice(i--, 1); - } - - parts.push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - 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 - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath || selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapBasepath = options.sourceMapBasepath; - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename.replace(/\\/g, '/'); - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename].substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), this._contentsMap[filename]); - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapFilename, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapFilename) { - sourceMapFilename = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapFilename = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapFilename) { - this._css.push("/*# sourceMappingURL=" + sourceMapFilename + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - location.port.length > 0 || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info; - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(newVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - if (newVars) { - env.useFileCache = true; - lessText += "\n" + newVars; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, newVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - if (newVars) { - lessText += "\n" + newVars; - } - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, newVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (newVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, newVars); -} - -function loadStyleSheets(callback, reload, newVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), newVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - var newVars = ""; - for (var name in record) { - newVars += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - less.refresh(false, newVars); -}; - -less.refresh = function (reload, newVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.info); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, newVars); - - loadStyles(newVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.5.0.min.js b/dist/less-1.5.0.min.js deleted file mode 100644 index ef4be9026..000000000 --- a/dist/less-1.5.0.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * LESS - Leaner CSS v1.5.0 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache v2 License. - * - * @licence - */ - -function require(a){return window.less[a.split("/")[1]]}function log(a,b){"development"==less.env&&"undefined"!=typeof console&&less.logLevel>=b&&console.log("less: "+a)}function extractId(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function errorConsole(a,b){var c="{line} {content}",d=a.filename||b,e=[],f=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+d+" ",g=function(a,b,d){void 0!==a.extract[b]&&e.push(c.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[b]))};a.extract?(g(a,0,""),g(a,1,"line"),g(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":\n"+e.join("\n")):a.stack&&(f+=a.stack),log(f,logLevel.errors)}function createCSS(a,b,c){var d=b.href||"",e="less:"+(b.title||extractId(d)),f=document.getElementById(e),g=!1,h=document.createElement("style");if(h.setAttribute("type","text/css"),b.media&&h.setAttribute("media",b.media),h.id=e,h.styleSheet)try{h.styleSheet.cssText=a}catch(i){throw new Error("Couldn't reassign styleSheet.cssText.")}else h.appendChild(document.createTextNode(a)),g=null!==f&&f.childNodes.length>0&&h.childNodes.length>0&&f.firstChild.nodeValue===h.firstChild.nodeValue;var j=document.getElementsByTagName("head")[0];if(null===f||g===!1){var k=b&&b.nextSibling||null;k?k.parentNode.insertBefore(h,k):j.appendChild(h)}if(f&&g===!1&&f.parentNode.removeChild(f),c&&cache){log("saving "+d+" to cache.",logLevel.info);try{cache.setItem(d,a),cache.setItem(d+":timestamp",c)}catch(i){log("failed to save",logLevel.errors)}}}function errorHTML(a,b){var c,d,e="less-error-message:"+extractId(b||""),f='
  • {content}
  • ',g=document.createElement("div"),h=[],i=a.filename||b,j=i.match(/([^\/]+(\?.*)?)$/)[1];g.id=e,g.className="less-error-message",d="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+"

    "+'

    in '+j+" ";var k=function(a,b,c){void 0!==a.extract[b]&&h.push(f.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.extract?(k(a,0,""),k(a,1,"line"),k(a,2,""),d+="on line "+a.line+", column "+(a.column+1)+":

    "+"
      "+h.join("")+"
    "):a.stack&&(d+="
    "+a.stack.split("\n").slice(1).join("
    ")),g.innerHTML=d,createCSS([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),g.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==less.env&&(c=setInterval(function(){document.body&&(document.getElementById(e)?document.body.replaceChild(g,document.getElementById(e)):document.body.insertBefore(g,document.body.firstChild),clearInterval(c))},10))}function error(a,b){less.errorReporting&&"html"!==less.errorReporting?"console"===less.errorReporting?errorConsole(a,b):"function"==typeof less.errorReporting&&less.errorReporting("add",a,b):errorHTML(a,b)}function removeErrorHTML(a){var b=document.getElementById("less-error-message:"+extractId(a));b&&b.parentNode.removeChild(b)}function removeErrorConsole(){}function removeError(a){less.errorReporting&&"html"!==less.errorReporting?"console"===less.errorReporting?removeErrorConsole(a):"function"==typeof less.errorReporting&&less.errorReporting("remove",a):removeErrorHTML(a)}function loadStyles(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function pathDiff(a,b){var c,d,e,f,g=extractUrlParts(a),h=extractUrlParts(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var f=getXMLHttpRequest(),g=isFileProtocol?less.fileAsync:less.async;"function"==typeof f.overrideMimeType&&f.overrideMimeType("text/css"),log("XHR: Getting '"+a+"'",logLevel.info),f.open("GET",a,g),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),isFileProtocol&&!less.fileAsync?0===f.status||f.status>=200&&f.status<300?c(f.responseText):d(f.status,a):g?f.onreadystatechange=function(){4==f.readyState&&e(f,c,d)}:e(f,c,d)}function loadFile(a,b,c,d,e){b&&b.currentDirectory&&!/^([a-z-]+:)?\//.test(a)&&(a=b.currentDirectory+a);var f=extractUrlParts(a,window.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(b?(h.entryPath=b.entryPath,h.rootpath=b.rootpath,h.rootFilename=b.rootFilename,h.relativeUrls=b.relativeUrls):(h.entryPath=f.path,h.rootpath=less.rootpath||f.path,h.rootFilename=g,h.relativeUrls=d.relativeUrls),h.relativeUrls&&(h.rootpath=d.rootpath?extractUrlParts(d.rootpath+pathDiff(f.path,h.entryPath)).path:f.path),d.useFileCache&&fileCache[g])try{var i=fileCache[g];e&&(i+="\n"+e),c(null,i,g,h,{lastModified:new Date})}catch(j){c(j,null,g)}else doXHR(g,d.mime,function(a,b){fileCache[g]=a;try{c(null,a,g,h,{lastModified:b})}catch(d){c(d,null,g)}},function(a,b){c({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function loadStyleSheet(a,b,c,d,e){var f=new less.tree.parseEnv(less);f.mime=a.type,e&&(f.useFileCache=!0),loadFile(a.href,null,function(e,g,h,i,j){if(j){j.remaining=d;var k=cache&&cache.getItem(h),l=cache&&cache.getItem(h+":timestamp");if(!c&&l&&j.lastModified&&new Date(j.lastModified).valueOf()===new Date(l).valueOf())return createCSS(k,a),j.local=!0,b(null,null,g,a,j,h),void 0}removeError(h),g?(f.currentFileInfo=i,new less.Parser(f).parse(g,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,g,a,j,h)}catch(c){b(c,null,null,a)}})):b(e,null,null,a,j,h)},f,e)}function loadStyleSheets(a,b,c){for(var d=0;dv&&(u[q]=u[q].slice(p-v),v=p)}function e(a){var b=a.charCodeAt(0);return 32===b||10===b||9===b}function f(a){var b,c;if(a instanceof Function)return a.call(w.parsers);if("string"==typeof a)b=o.charAt(p)===a?a:null,c=1,d();else{if(d(),!(b=a.exec(u[q])))return null;c=b[0].length}return b?(g(c),"string"==typeof b?b:1===b.length?b[0]:b):void 0}function g(a){for(var b=p,c=q,d=p+u[q].length,f=p+=a;d>p&&e(o.charAt(p));)p++;return u[q]=u[q].slice(a+(p-f)),v=p,0===u[q].length&&q=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function m(a,b,c){var d=c.currentFileInfo.filename;return"browser"!==less.mode&&"rhino"!==less.mode&&(d=require("path").resolve(d)),{lineNumber:l(a,b).line+1,fileName:d}}function n(a,b){var c=k(a,b),d=l(a.index,c),e=d.line,f=d.column,g=a.call&&l(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var o,p,q,r,s,t,u,v,w,x=a&&a.filename;a instanceof tree.parseEnv||(a=new tree.parseEnv(a));var y=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files||d===x;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};less.Parser.importer?less.Parser.importer(b,c,g,a):less.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new tree.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new less.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}};return n.prototype=new Error,n.prototype.constructor=n,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,w={imports:y,parse:function(b,c){var d,e,g,h=null;if(p=q=v=t=0,o=b.replace(/\r\n/g,"\n"),o=o.replace(/^\uFEFF/,""),w.imports.contents[a.currentFileInfo.filename]=o,u=function(b){for(var c,d,e,f,g=0,i=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,j=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,k=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,l=0,m=b[0],p=0;p0?"missing closing `}`":"missing opening `{`",filename:a.currentFileInfo.filename},a)),b.map(function(a){return a.join("")})}([[]]),h)return c(new n(h,a));try{d=new tree.Ruleset([],f(this.parsers.primary)),d.root=!0,d.firstRoot=!0}catch(i){return c(new n(i,a))}if(d.toCSS=function(b){return function(c,d){c=c||{};var e,f,g=new tree.evalEnv(c);"object"!=typeof d||Array.isArray(d)||(d=Object.keys(d).map(function(a){var b=d[a];return b instanceof tree.Value||(b instanceof tree.Expression||(b=new tree.Expression([b])),b=new tree.Value([b])),new tree.Rule("@"+a,b,!1,null,0)}),g.frames=[new tree.Ruleset(null,d)]);try{e=b.call(this,g),(new tree.joinSelectorVisitor).run(e),(new tree.processExtendsVisitor).run(e),new tree.toCSSVisitor({compress:Boolean(c.compress)}).run(e),c.sourceMap&&(e=new tree.sourceMapOutput({writeSourceMap:c.writeSourceMap,rootNode:e,contentsMap:w.imports.contents,sourceMapFilename:c.sourceMapFilename,outputFilename:c.sourceMapOutputFilename,sourceMapBasepath:c.sourceMapBasepath,sourceMapRootpath:c.sourceMapRootpath,outputSourceFiles:c.outputSourceFiles,sourceMapGenerator:c.sourceMapGenerator})),f=e.toCSS({compress:Boolean(c.compress),dumpLineNumbers:a.dumpLineNumbers,strictUnits:Boolean(c.strictUnits)})}catch(h){throw new n(h,a)}return c.cleancss&&"node"===less.mode?require("clean-css").process(f):c.compress?f.replace(/(^(\s)+)|((\s)+$)/g,""):f}}(d.eval),p57||43>b||47===b||44==b))return(a=f(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))?new tree.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return(a=f(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))?new tree.UnicodeDescriptor(a[0]):void 0},javascript:function(){var b,c,d=p;return"~"===o.charAt(d)&&(d++,c=!0),"`"===o.charAt(d)?(void 0===a.javascriptEnabled||a.javascriptEnabled||i("You are using JavaScript, which has been disabled."),c&&f("~"),(b=f(/^`([^`]*)`/))?new tree.JavaScript(b[1],p,c):void 0):void 0}},variable:function(){var a;return"@"===o.charAt(p)&&(a=f(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e=p,g=[];if(f(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=[];;){if(d=f(/^(all)(?=\s*(\)|,))/))break;if(c=f(this.element),!c)break;b.push(c)}d=d&&d[1],g.push(new tree.Extend(new tree.Selector(b),d,e))}while(f(","));return h(/^\)/),a&&h(/^;/),g}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var d,e,g,i=[],k=p,l=o.charAt(p),m=!1;if("."===l||"#"===l){for(b();d=f(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);)i.push(new tree.Element(e,d,p,a.currentFileInfo)),e=f(">");return f("(")&&(g=this.mixin.args.call(this,!0).args,h(")")),g=g||[],f(this.important)&&(m=!0),i.length>0&&(f(";")||j("}"))?new tree.mixin.Call(i,g,k,a.currentFileInfo,m):(c(),void 0)}},args:function(a){for(var b,c,d,e,g,j,k=[],l=[],m=[],n={args:null,variadic:!1};;){if(a)j=f(this.expression);else{if(f(this.comments),"."===o.charAt(p)&&f(/^\.{3}/)){n.variadic=!0,f(";")&&!b&&(b=!0),(b?l:m).push({variadic:!0});break}j=f(this.entities.variable)||f(this.entities.literal)||f(this.entities.keyword)}if(!j)break;e=null,j.throwAwayComments&&j.throwAwayComments(),g=j;var q=null;if(a?1==j.value.length&&(q=j.value[0]):q=j,q&&q instanceof tree.Variable)if(f(":"))k.length>0&&(b&&i("Cannot mix ; and , as delimiter types"),c=!0),g=h(this.expression),e=d=q.name;else{if(!a&&f(/^\.{3}/)){n.variadic=!0,f(";")&&!b&&(b=!0),(b?l:m).push({name:j.name,variadic:!0});break}a||(d=e=q.name,g=null)}g&&k.push(g),m.push({name:e,value:g}),f(",")||(f(";")||b)&&(c&&i("Cannot mix ; and , as delimiter types"),b=!0,k.length>1&&(g=new tree.Value(k)),l.push({name:d,value:g}),d=null,k=[],c=!1)}return n.args=b?l:m,n},definition:function(){var a,d,e,g,i=[],k=!1;if(!("."!==o.charAt(p)&&"#"!==o.charAt(p)||j(/^[^{]*\}/))&&(b(),d=f(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=d[1];var l=this.mixin.args.call(this,!1);if(i=l.args,k=l.variadic,f(")")||(t=p,c()),f(this.comments),f(/^when/)&&(g=h(this.conditions,"expected condition")),e=f(this.block))return new tree.mixin.Definition(a,i,e,g,k);c()}}},entity:function(){return f(this.entities.literal)||f(this.entities.variable)||f(this.entities.url)||f(this.entities.call)||f(this.entities.keyword)||f(this.entities.javascript)||f(this.comment)},end:function(){return f(";")||j("}")},alpha:function(){var a;if(f(/^\(opacity=/i))return(a=f(/^\d+/)||f(this.entities.variable))?(h(")"),new tree.Alpha(a)):void 0},element:function(){var b,c,d;return c=f(this.combinator),b=f(/^(?:\d+\.\d+|\d+)%/)||f(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||f("*")||f("&")||f(this.attribute)||f(/^\([^()@]+\)/)||f(/^[\.#](?=@)/)||f(this.entities.variableCurly),b||f("(")&&(d=f(this.selector))&&f(")")&&(b=new tree.Paren(d)),b?new tree.Element(c,b,p,a.currentFileInfo):void 0},combinator:function(){var a=o.charAt(p);if(">"===a||"+"===a||"~"===a||"|"===a){for(p++;o.charAt(p).match(/\s/);)p++;return new tree.Combinator(a)}return o.charAt(p-1).match(/\s/)?new tree.Combinator(" "):new tree.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,g,j,k=[],l=[];(b&&(e=f(this.extend))||b&&(g=f(/^when/))||(c=f(this.element)))&&(g?j=h(this.conditions,"expected condition"):j?i("CSS guard can only be used at the end of selector"):e?l.push.apply(l,e):(l.length&&i("Extend can only be used at the end of selector"),d=o.charAt(p),k.push(c),c=null),"{"!==d&&"}"!==d&&";"!==d&&","!==d&&")"!==d););return k.length>0?new tree.Selector(k,l,j,p,a.currentFileInfo):(l.length&&i("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){var a,b,c;if(f("["))return(a=f(this.entities.variableCurly))||(a=h(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),(c=f(/^[|~*$^]?=/))&&(b=f(this.entities.quoted)||f(/^[0-9]+%/)||f(/^[\w-]+/)||f(this.entities.variableCurly)),h("]"),new tree.Attribute(a,c,b)},block:function(){var a;return f("{")&&(a=f(this.primary))&&f("}")?a:void 0},ruleset:function(){var d,e,g,h=[];for(b(),a.dumpLineNumbers&&(g=m(p,o,a));(d=f(this.lessSelector))&&(h.push(d),f(this.comments),f(","));)d.condition&&i("Guards are only currently allowed on a single selector."),f(this.comments);if(h.length>0&&(e=f(this.block))){var j=new tree.Ruleset(h,e,a.strictImports);return a.dumpLineNumbers&&(j.debugInfo=g),j}t=p,c()},rule:function(d){var e,g,h,i=o.charAt(p),j=!1;if(b(),"."!==i&&"#"!==i&&"&"!==i&&(e=f(this.variable)||f(this.ruleProperty))){if(g=d||!a.compress&&"@"!==e.charAt(0)?f(this.anonymousValue)||f(this.value):f(this.value)||f(this.anonymousValue),h=f(this.important),"+"===e[e.length-1]&&(j=!0,e=e.substr(0,e.length-1)),g&&f(this.end))return new tree.Rule(e,g,h,j,s,a.currentFileInfo);if(t=p,c(),g&&!d)return this.rule(!0)}},anonymousValue:function(){var a;return(a=/^([^@+\/'"*`(;{}-]*);/.exec(u[q]))?(p+=a[0].length-1,new tree.Anonymous(a[1])):void 0},"import":function(){var d,e,g=p;b();var h=f(/^@import?\s+/),i=(h?f(this.importOptions):null)||{};return h&&(d=f(this.entities.quoted)||f(this.entities.url))&&(e=f(this.mediaFeatures),f(";"))?(e=e&&new tree.Value(e),new tree.Import(d,e,i,g,a.currentFileInfo)):(c(),void 0)},importOptions:function(){var a,b,c,d={};if(!f("("))return null;do if(a=f(this.importOption)){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!f(","))break}while(a);return h(")"),d},importOption:function(){var a=f(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=[];do if(b=f(this.entities.keyword)||f(this.entities.variable))d.push(b);else if(f("(")){if(c=f(this.property),b=f(this.value),!f(")"))return null;if(c&&b)d.push(new tree.Paren(new tree.Rule(c,b,null,null,p,a.currentFileInfo,!0)));else{if(!b)return null;d.push(new tree.Paren(b))}}while(b);return d.length>0?new tree.Expression(d):void 0},mediaFeatures:function(){var a,b=[];do if(a=f(this.mediaFeature)){if(b.push(a),!f(","))break}else if((a=f(this.entities.variable))&&(b.push(a),!f(",")))break;while(a);return b.length>0?b:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=m(p,o,a)),f(/^@media/)&&(b=f(this.mediaFeatures),c=f(this.block))?(d=new tree.Media(c,b,p,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var d,e,g,h,i,j,k,l;if("@"===o.charAt(p)){if(e=f(this["import"])||f(this.media))return e;if(b(),d=f(/^@[a-z-]+/)){switch(h=d,"-"==d.charAt(1)&&d.indexOf("-",2)>0&&(h="@"+d.slice(d.indexOf("-",2)+1)),h){case"@font-face":i=!0;break;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":i=!0;break;case"@host":case"@page":case"@document":case"@supports":case"@keyframes":i=!0,j=!0;break;case"@namespace":k=!0}if(j&&(l=(f(/^[^{]+/)||"").trim(),l&&(d+=" "+l)),i){if(g=f(this.block))return new tree.Directive(d,g,p,a.currentFileInfo)}else if((e=k?f(this.expression):f(this.entity))&&f(";")){var n=new tree.Directive(d,e,p,a.currentFileInfo);return a.dumpLineNumbers&&(n.debugInfo=m(p,o,a)),n}c()}}},value:function(){for(var a,b=[];(a=f(this.expression))&&(b.push(a),f(",")););return b.length>0?new tree.Value(b):void 0},important:function(){return"!"===o.charAt(p)?f(/^! *important/):void 0},sub:function(){var a,b;return f("(")&&(a=f(this.addition))?(b=new tree.Expression([a]),h(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,g;if(a=f(this.operand)){for(g=e(o.charAt(p-1));!j(/^\/[*\/]/)&&(c=f("/")||f("*"))&&(b=f(this.operand));)a.parensInOp=!0,b.parensInOp=!0,d=new tree.Operation(c,[d||a,b],g),g=e(o.charAt(p-1));return d||a}},addition:function(){var a,b,c,d,g;if(a=f(this.multiplication)){for(g=e(o.charAt(p-1));(c=f(/^[-+]\s+/)||!g&&(f("+")||f("-")))&&(b=f(this.multiplication));)a.parensInOp=!0,b.parensInOp=!0,d=new tree.Operation(c,[d||a,b],g),g=e(o.charAt(p-1));return d||a}},conditions:function(){var a,b,c,d=p;if(a=f(this.condition)){for(;j(/^,\s*(not\s*)?\(/)&&f(",")&&(b=f(this.condition));)c=new tree.Condition("or",c||a,b,d);return c||a}},condition:function(){var a,b,c,d,e=p,g=!1;return f(/^not/)&&(g=!0),h("("),(a=f(this.addition)||f(this.entities.keyword)||f(this.entities.quoted))?((d=f(/^(?:>=|<=|=<|[<=>])/))?(b=f(this.addition)||f(this.entities.keyword)||f(this.entities.quoted))?c=new tree.Condition(d,a,b,e,g):i("expected expression"):c=new tree.Condition("=",a,new tree.Keyword("true"),e,g),h(")"),f(/^and/)?new tree.Condition("and",c,f(this.condition)):c):void 0},operand:function(){var a,b=o.charAt(p+1);"-"!==o.charAt(p)||"@"!==b&&"("!==b||(a=f("-"));var c=f(this.sub)||f(this.entities.dimension)||f(this.entities.color)||f(this.entities.variable)||f(this.entities.call);return a&&(c.parensInOp=!0,c=new tree.Negative(c)),c},expression:function(){for(var a,b,c=[];a=f(this.addition)||f(this.entity);)c.push(a),!j(/^\/[\/*]/)&&(b=f("/"))&&c.push(new tree.Anonymous(b));return c.length>0?new tree.Expression(c):void 0},property:function(){var a;return(a=f(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/))?a[1]:void 0},ruleProperty:function(){var a;return(a=f(/^(\*?-?[_a-zA-Z0-9-]+)\s*(\+?)\s*:/))?a[1]+(a[2]||""):void 0}}}},function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b,c){return b instanceof a.Dimension&&b.unit.is("%")?parseFloat(b.value*c/100):d(b)}function d(b){if(b instanceof a.Dimension)return parseFloat(b.unit.is("%")?b.value/100:b.value);if("number"==typeof b)return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function e(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,e,f,g){var h=[b,e,f].map(function(a){return c(a,256)});return g=d(g),new a.Color(h,g)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,f){function g(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?i+6*(h-i)*a:1>2*a?h:2>3*a?i+6*(h-i)*(2/3-a):i}a=d(a)%360/360,b=e(d(b)),c=e(d(c)),f=e(d(f));var h=.5>=c?c*(b+1):c+b-c*b,i=2*c-h;return this.rgba(255*g(a+1/3),255*g(a),255*g(a-1/3),f)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,e){a=360*(d(a)%360/360),b=d(b),c=d(c),e=d(e);var f,g;f=Math.floor(a/60%6),g=a/60-f;var h=[c,c*(1-b),c*(1-g*b),c*(1-(1-g)*b)],i=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*h[i[f][0]],255*h[i[f][1]],255*h[i[f][2]],e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(100*b.toHSL().s),"%")},lightness:function(b){return new a.Dimension(Math.round(100*b.toHSL().l),"%")},hsvhue:function(b){return new a.Dimension(Math.round(b.toHSV().h))},hsvsaturation:function(b){return new a.Dimension(Math.round(100*b.toHSV().s),"%")},hsvvalue:function(b){return new a.Dimension(Math.round(100*b.toHSV().v),"%")},red:function(b){return new a.Dimension(b.rgb[0])},green:function(b){return new a.Dimension(b.rgb[1])},blue:function(b){return new a.Dimension(b.rgb[2])},alpha:function(b){return new a.Dimension(b.toHSL().a)},luma:function(b){return new a.Dimension(Math.round(100*b.luma()*b.alpha),"%")},saturate:function(a,c){if(!a.rgb)return null;var d=a.toHSL();return d.s+=c.value/100,d.s=e(d.s),b(d)},desaturate:function(a,c){var d=a.toHSL();return d.s-=c.value/100,d.s=e(d.s),b(d)},lighten:function(a,c){var d=a.toHSL();return d.l+=c.value/100,d.l=e(d.l),b(d)},darken:function(a,c){var d=a.toHSL();return d.l-=c.value/100,d.l=e(d.l),b(d)},fadein:function(a,c){var d=a.toHSL();return d.a+=c.value/100,d.a=e(d.a),b(d)},fadeout:function(a,c){var d=a.toHSL();return d.a-=c.value/100,d.a=e(d.a),b(d)},fade:function(a,c){var d=a.toHSL();return d.a=c.value/100,d.a=e(d.a),b(d)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=0>e?360+e:e,b(d)},mix:function(b,c,d){d||(d=new a.Dimension(50));var e=d.value/100,f=2*e-1,g=b.toHSL().a-c.toHSL().a,h=((-1==f*g?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},contrast:function(a,b,c,e){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var f=c;c=b,b=f}return e="undefined"==typeof e?.43:d(e),a.luma()*a.alphah.value)&&(j[e]=f)):(k[i]=j.length,j.push(f))):j.push(f);return 1==j.length?j[0]:(c=j.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new a.Anonymous((b?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(b){return new a.Anonymous(b.toARGB())},percentage:function(b){return new a.Dimension(100*b.value,"%")},color:function(b){if(b instanceof a.Quoted){var c,d=b.value;if(c=a.Color.fromKeyword(d))return c;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(d))return new a.Color(d.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(b){return this._isa(b,a.Color)},isnumber:function(b){return this._isa(b,a.Dimension)},isstring:function(b){return this._isa(b,a.Quoted)},iskeyword:function(b){return this._isa(b,a.Keyword)},isurl:function(b){return this._isa(b,a.URL) -},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(b,c){return b instanceof a.Dimension&&b.unit.is(c.value||c)?a.True:a.False},_isa:function(b,c){return b instanceof c?a.True:a.False},multiply:function(a,b){var c=a.rgb[0]*b.rgb[0]/255,d=a.rgb[1]*b.rgb[1]/255,e=a.rgb[2]*b.rgb[2]/255;return this.rgb(c,d,e)},screen:function(a,b){var c=255-(255-a.rgb[0])*(255-b.rgb[0])/255,d=255-(255-a.rgb[1])*(255-b.rgb[1])/255,e=255-(255-a.rgb[2])*(255-b.rgb[2])/255;return this.rgb(c,d,e)},overlay:function(a,b){var c=a.rgb[0]<128?2*a.rgb[0]*b.rgb[0]/255:255-2*(255-a.rgb[0])*(255-b.rgb[0])/255,d=a.rgb[1]<128?2*a.rgb[1]*b.rgb[1]/255:255-2*(255-a.rgb[1])*(255-b.rgb[1])/255,e=a.rgb[2]<128?2*a.rgb[2]*b.rgb[2]/255:255-2*(255-a.rgb[2])*(255-b.rgb[2])/255;return this.rgb(c,d,e)},softlight:function(a,b){var c=b.rgb[0]*a.rgb[0]/255,d=c+a.rgb[0]*(255-(255-a.rgb[0])*(255-b.rgb[0])/255-c)/255;c=b.rgb[1]*a.rgb[1]/255;var e=c+a.rgb[1]*(255-(255-a.rgb[1])*(255-b.rgb[1])/255-c)/255;c=b.rgb[2]*a.rgb[2]/255;var f=c+a.rgb[2]*(255-(255-a.rgb[2])*(255-b.rgb[2])/255-c)/255;return this.rgb(d,e,f)},hardlight:function(a,b){var c=b.rgb[0]<128?2*b.rgb[0]*a.rgb[0]/255:255-2*(255-b.rgb[0])*(255-a.rgb[0])/255,d=b.rgb[1]<128?2*b.rgb[1]*a.rgb[1]/255:255-2*(255-b.rgb[1])*(255-a.rgb[1])/255,e=b.rgb[2]<128?2*b.rgb[2]*a.rgb[2]/255:255-2*(255-b.rgb[2])*(255-a.rgb[2])/255;return this.rgb(c,d,e)},difference:function(a,b){var c=Math.abs(a.rgb[0]-b.rgb[0]),d=Math.abs(a.rgb[1]-b.rgb[1]),e=Math.abs(a.rgb[2]-b.rgb[2]);return this.rgb(c,d,e)},exclusion:function(a,b){var c=a.rgb[0]+b.rgb[0]*(255-a.rgb[0]-a.rgb[0])/255,d=a.rgb[1]+b.rgb[1]*(255-a.rgb[1]-a.rgb[1])/255,e=a.rgb[2]+b.rgb[2]*(255-a.rgb[2]-a.rgb[2])/255;return this.rgb(c,d,e)},average:function(a,b){var c=(a.rgb[0]+b.rgb[0])/2,d=(a.rgb[1]+b.rgb[1])/2,e=(a.rgb[2]+b.rgb[2])/2;return this.rgb(c,d,e)},negation:function(a,b){var c=255-Math.abs(255-b.rgb[0]-a.rgb[0]),d=255-Math.abs(255-b.rgb[1]-a.rgb[1]),e=255-Math.abs(255-b.rgb[2]-a.rgb[2]);return this.rgb(c,d,e)},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(b){var c=Array.isArray(b.value)?b.value.length:1;return new a.Dimension(c)},"data-uri":function(b,c){if("undefined"!=typeof window)return new a.URL(c||b,this.currentFileInfo).eval(this.env);var d=b.value,e=c&&c.value,f=require("fs"),g=require("path"),h=!1;if(arguments.length<2&&(e=d),this.env.isPathRelative(e)&&(e=this.currentFileInfo.relativeUrls?g.join(this.currentFileInfo.currentDirectory,e):g.join(this.currentFileInfo.entryPath,e)),arguments.length<2){var i;try{i=require("mime")}catch(j){i=a._mime}d=i.lookup(e);var k=i.charsets.lookup(d);h=["US-ASCII","UTF-8"].indexOf(k)<0,h&&(d+=";base64")}else h=/;base64$/.test(d);var l=f.readFileSync(e),m=32,n=parseInt(l.length/1024,10);if(n>=m&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",e,n,m),new a.URL(c||b,this.currentFileInfo).eval(this.env);l=h?l.toString("base64"):encodeURIComponent(l);var o="'data:"+d+","+l+"'";return new a.URL(new a.Anonymous(o))},"svg-gradient":function(b){function c(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&c();var d,e,f,g,h,i,j,k=Array.prototype.slice.call(arguments,1),l="linear",m='x="0" y="0" width="1" height="1"',n=!0,o={compress:!1},p=b.toCSS(o);switch(p){case"to bottom":d='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":d='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":d='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":d='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":l="radial",d='cx="50%" cy="50%" r="75%"',m='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(e='<'+l+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+d+">",f=0;fj?' stop-opacity="'+j+'"':"")+"/>";if(e+=""+"',n)try{e=new Buffer(e).toString("base64")}catch(q){n=!1}return e="'data:image/svg+xml"+(n?";base64":"")+","+e+"'",new a.URL(new a.Anonymous(e))}},a._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(b){var c=require("path").extname(b),d=a._mime._types[c];if(void 0===d)throw new Error('Optional dependency "mime" is required for '+c);return d},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};for(var f=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],g=function(a,b){return function(c){return null!=b&&(c=c.unify()),this._math(Math[a],b,c)}},h=0;h1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){b.add(a.compress?"{":" {\n"),a.tabLevel=(a.tabLevel||0)+1;for(var d=a.compress?"":Array(a.tabLevel+1).join(" "),e=a.compress?"":Array(a.tabLevel).join(" "),f=0;fb?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(require("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(require("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args=a.visit(this.args)},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;cf;f++)e[f]=a.operate(b,c,this.rgb[f],d.rgb[f]);return new a.Color(e,this.alpha+d.alpha)},toRGB:function(){return"#"+this.rgb.map(function(a){return a=Math.round(a),a=(a>255?255:0>a?0:a).toString(16),1===a.length?"0"+a:a}).join("")},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){var a=[Math.round(255*this.alpha)].concat(this.rgb);return"#"+a.map(function(a){return a=Math.round(a),a=(a>255?255:0>a?0:a).toString(16),1===a.length?"0"+a:a}).join("")},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(c){if(a.colors.hasOwnProperty(c))return new a.Color(a.colors[c].slice(1));if(c===b){var d=new a.Color([0,0,0],0);return d.isTransparentKeyword=!0,d}}}(require("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(require("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(require("../tree")),function(a){a.Dimension=function(b,c){this.value=parseFloat(b),this.unit=c&&c instanceof a.Unit?c:new a.Unit(c?[c]:void 0)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.value,d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&this.unit.isLength())return b.add(d),void 0;c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:12*(.0254/72)},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(require("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset([],c)],this.rules[0].allowImports=!0):this.value=c,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules=a.visit(this.rules),this.value=a.visit(this.value)},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|"},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(require("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value=a.visit(this.value)},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(require("../tree")),function(a){a.Import=function(a,b,c,d,e){if(this.options=c,this.index=d,this.path=a,this.features=b,this.currentFileInfo=e,void 0!==this.options.less||this.options.inline)this.css=!this.options.less||this.options.inline;else{var f=this.getPath();f&&/css([\?;].*)?$/.test(f)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features=a.visit(this.features),this.path=a.visit(this.path),this.options.inline||(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var b=this.path.value;return void 0!==this.css||/(\.[a-z]*$)|([\?;].*)$/.test(b)?b:b+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip)return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset([],this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(require("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}for(var h in b.frames[0].variables())e[h.slice(1)]={value:b.frames[0].variables()[h].value,toJS:function(){return this.value.eval(b).toCSS()}};try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(require("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(require("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features=a.visit(this.features),this.rules=a.visit(this.rules)},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media([],[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo);return[new a.Selector([b],null,null,this.index,this.currentFileInfo)]},markReferenced:function(){var a,b=this.rules[0].rules;for(this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(require("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(require("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c,d=this.operands[0].eval(b),e=this.operands[1].eval(b);if(b.isMathOn()){if(d instanceof a.Dimension&&e instanceof a.Color){if("*"!==this.op&&"+"!==this.op)throw{type:"Operation",message:"Can't substract or divide a color from a number"};c=e,e=d,d=c}if(!d.operate)throw{type:"Operation",message:"Operation on an invalid type"};return d.operate(b,this.op,e)}return new a.Operation(this.op,[d,e],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(require("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(require("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(require("../tree")),function(a){a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable="@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(b){var c=!1;"font"!==this.name||b.strictMath||(c=!0,b.strictMath=!0);try{return new a.Rule(this.name,this.value.eval(b),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}finally{c&&(b.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(require("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){if(this.paths)for(var b=0;bf.selectors[g].elements.length?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(1)),c)):e.push(f);break}}),this._lookups[f]=e)},genCSS:function(b,c){var d,e,f,g,h,i=[],j=[],k=!0;b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l=b.compress?"":Array(b.tabLevel+1).join(" "),m=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;d0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",0,j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(require("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b||[],this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements=a.visit(this.elements),this.extendList=a.visit(this.extendList),this.condition=a.visit(this.condition)},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,this.condition,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e},match:function(a){var b,c,d,e,f=this.elements,g=f.length;if(b=a.elements.slice(a.elements.length&&"&"===a.elements[0].value?1:0),c=b.length,d=Math.min(g,c),0===c||c>g)return!1;for(e=0;d>e;e++)if(f[e].value!==b[e].value)return!1;return!0},eval:function(a){var b=this.condition&&this.condition.eval(a);return this.createDerived(this.elements.map(function(b){return b.eval(a)}),this.extendList.map(function(b){return b.eval(a)}),b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;c0)&&e.splice(0,0,b);else{b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;c0&&b.accept(this._visitor),c.visitDeeper=!1,this._mergeRules(b.rules),this._removeDuplicateRules(b.rules),b.rules.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d},_mergeRules:function(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}(require("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[];for(c=0;c100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},inInheritanceChain:function(a,b){if(a===b)return!0;if(b.parents){if(this.inInheritanceChain(a,b.parents[0]))return!0;if(this.inInheritanceChain(a,b.parents[1]))return!0}return!1},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(require("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapBasepath=a.sourceMapBasepath,this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||require("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a.replace(/\\/g,"/")},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename].substring(0,c);f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var c,d=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapFilename&&(c=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(d):c="data:application/json,"+encodeURIComponent(d),c&&this._css.push("/*# sourceMappingURL="+c+" */")}return this._css.join("")}}(require("./tree"));var isFileProtocol=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);less.env=less.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port.length>0||isFileProtocol?"development":"production");var logLevel={info:2,errors:1,none:0};if(less.logLevel="undefined"!=typeof less.logLevel?less.logLevel:logLevel.info,less.async=less.async||!1,less.fileAsync=less.fileAsync||!1,less.poll=less.poll||(isFileProtocol?1e3:1500),less.functions)for(var func in less.functions)less.tree.functions[func]=less.functions[func];var dumpLineNumbers=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);dumpLineNumbers&&(less.dumpLineNumbers=dumpLineNumbers[1]);var typePattern=/^text\/(x-)?less$/,cache=null,fileCache={};if(less.watch=function(){return less.watchMode||(less.env="development",initRunningMode()),this.watchMode=!0},less.unwatch=function(){return clearInterval(less.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&less.watch(),"development"!=less.env)try{cache="undefined"==typeof window.localStorage?null:window.localStorage}catch(_){}var links=document.getElementsByTagName("link");less.sheets=[];for(var i=0;i - * Licensed under the Apache v2 License. - * - * @licence - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = chunks[j], memo = i, current = i; } - function restore() { chunks[j] = temp, i = memo, current = i; } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, length; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break; } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(chunks[j]); - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, line, lines, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - parser.imports.contents[env.currentFileInfo.filename] = input; - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': - if (!inParam) { - level++; - chunk.push(c); - break; - } - /* falls through */ - case '}': - if (!inParam) { - level--; - chunk.push(c); - chunks[++j] = chunk = []; - break; - } - /* falls through */ - case '(': - if (!inParam) { - inParam = true; - chunk.push(c); - break; - } - /* falls through */ - case ')': - if (inParam) { - inParam = false; - chunk.push(c); - break; - } - /* falls through */ - default: - chunk.push(c); - } - - i++; - } - if (level !== 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join(''); }); - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - evaldRoot = evaluate.call(this, evalEnv); - - new(tree.joinSelectorVisitor)() - .run(evaldRoot); - - new(tree.processExtendsVisitor)() - .run(evaldRoot); - - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - .run(evaldRoot); - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'); - //TODO would be nice for no advanced to be an option - return new CleanCSS({keepSpecialComments: '*', processImport: false, noRebase: true, noAdvanced: true}).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true, i, env.currentFileInfo); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(comment = $(this.comment)) { - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null; } - else { i += name.length; } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { - break; - } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) { - return; - } - - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - /*jshint eqnull:true */ - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $('~'); } - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i, env.currentFileInfo)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comments); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comments); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) { return; } - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++; } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var e, elements = [], c, extend, extendList = [], when, condition; - - while ((isLess && (extend = $(this.extend))) || (isLess && (when = $(/^when/))) || (e = $(this.element))) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e); - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var key, val, op; - - if (! $('[')) { return; } - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[0-9]+%/) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (s = $(this.lessSelector)) { - selectors.push(s); - $(this.comments); - if (! $(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - $(this.comments); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - if (name = $(this.variable) || $(this.ruleProperty)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - - important = $(this.important); - if (name[name.length-1] === "+") { - merge = true; - name = name.substr(0, name.length - 1); - } - - if (value && $(this.end)) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break; } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = ($(this.entities.keyword) || $(this.entities.variable))) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break; } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break; } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules, i, env.currentFileInfo); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break; } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while (peek(/^,\s*(not\s*)?\(/) && $(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true; } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|<=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-'); } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = []; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/)) { - return name[1]; - } - }, - ruleProperty: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*(\+?)\s*:/)) { - return name[1] + (name[2] || ""); - } - } - } - }; -}; - - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - /*jshint eqnull:true */ - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - // only works in node, needs interface to what is supported in environment - try { - returner = new Buffer(returner).toString('base64'); - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - /*jshint eqnull:true */ - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - output.add((env.compress ? '{' : ' {\n')); - env.tabLevel = (env.tabLevel || 0) + 1; - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); - for(var i = 0; i < rules.length; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - output.add(env.compress ? '' : '\n'); - } - env.tabLevel--; - output.add(tabSetStr + "}"); -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { return this; }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - /*jshint eqnull:true */ - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - - // 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 (this.alpha < 1.0) { - if (this.alpha === 0 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toRGB: function () { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)([], value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.rules = visitor.visit(this.rules); - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - if (!this.options.inline) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.rules = visitor.visit(this.rules); - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); - return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - //if (this.important) { - // isImportant = env.isImportant; - // env.isImportant = true; - //} - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - //if (this.important) { - // env.isImportant = isImportant; - //} - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { type: "Operation", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - for(var i = 0; i < this.paths.length; i++) { - this.paths[i] = visitor.visit(this.paths[i]); - } - } else { - this.selectors = visitor.visit(this.selectors); - } - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env); }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - var rule; - var i; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables; } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - debugInfo, // Line number debugging - rule, - firstRuleset = true, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - for(i = 0; i < this.paths.length; i++) { - path = this.paths[i]; - env.firstSelector = true; - for(j = 0; j < path.length; j++) { - path[j].genCSS(env, output); - env.firstSelector = false; - } - if (i + 1 < this.paths.length) { - output.add(env.compress ? ',' : (',\n' + tabSetStr)); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - for (i = 0; i < rulesetNodes.length; i++) { - if (ruleNodes.length && firstRuleset) { - output.add((env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr)); - } - if (!firstRuleset) { - output.add((env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr)); - } - firstRuleset = false; - rulesetNodes[i].genCSS(env, output); - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList || []; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList); - this.condition = visitor.visit(this.condition); - }, - createDerived: function(elements, extendList, evaldCondition) { - /*jshint eqnull:true */ - evaldCondition = evaldCondition != null ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return 0; - } - } - } - return max; // return number of matched selectors - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env); - - return this.createDerived(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - }), evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple'// whether we are currently importing multiple copies - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - evald = this.flatten(evald); - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - }, - doAccept: function (node) { - node.accept(this); - }, - flatten: function(arr, master) { - return arr.reduce(this.flattenReduce.bind(this), master || []); - }, - flattenReduce: function(sum, element) { - if (element instanceof Array) { - sum = this.flatten(element, sum); - } else { - sum.push(element); - } - return sum; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.selectors = rulesetNode.selectors.filter(function(selector) { return selector.getIsOutput(); }); - if (rulesetNode.selectors.length === 0) { - rulesetNode.rules.length = 0; - } - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - return false; - } - }); - - // Compile rules and rulesets - for (var i = 0; i < rulesetNode.rules.length; i++) { - rule = rulesetNode.rules[i]; - - if (rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - rulesetNode.rules.splice(i, 1); - i--; - continue; - } - } - // accept the visitor to remove rules and refactor itself - // then we can decide now whether we want it or not - if (rulesetNode.rules.length > 0) { - rulesetNode.accept(this._visitor); - } - visitArgs.visitDeeper = false; - - this._mergeRules(rulesetNode.rules); - this._removeDuplicateRules(rulesetNode.rules); - - // now decide whether we keep the ruleset - if (rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || rulesetNode.rules.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - parts = groups[key] = []; - } else { - rules.splice(i--, 1); - } - - parts.push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - 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 - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath || selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - this._sourceMapBasepath = options.sourceMapBasepath; - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename.replace(/\\/g, '/'); - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename].substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), this._contentsMap[filename]); - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info; - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; -var varsPre = ""; - -function log(str, level) { - if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(newVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (newVars || varsPre) { - env.useFileCache = true; - - lessText = varsPre + lessText; - - if (newVars) { - lessText += "\n" + newVars; - } - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, newVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - if (newVars) { - lessText += "\n" + newVars; - } - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - data = varsPre + data; - - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, newVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (newVars || varsPre) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, newVars); -} - -function loadStyleSheets(callback, reload, newVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), newVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - -function serializeVars(vars) { - var s = ""; - - for (var name in vars) { - s += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((vars[name].slice(-1) === ';')? vars[name] : vars[name] +';'); - } - - return s; -} - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - return this.watchMode = true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, serializeVars(record)); -}; - -less.refresh = function (reload, newVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.info); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, newVars); - - loadStyles(newVars); -}; - -if (less.globalVars) { - varsPre = serializeVars(less.globalVars) + "\n"; -} - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.5.1.min.js b/dist/less-1.5.1.min.js deleted file mode 100644 index 3f833f71c..000000000 --- a/dist/less-1.5.1.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * LESS - Leaner CSS v1.5.1 - * http://lesscss.org - * - * Copyright (c) 2009-2013, Alexis Sellier - * Licensed under the Apache v2 License. - * - * @licence - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"development"==w.env&&"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(k){d("failed to save",z.errors)}}}function h(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function i(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):h(a,b)}function j(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function k(){}function l(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?k(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):j(a)}function m(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function o(a,b){var c,d,e,f,g=n(a),h=n(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=p(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.info),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function r(b,c,d,e,f){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var g=n(b,a.location.href),h=g.url,i={currentDirectory:g.path,filename:h};if(c?(i.entryPath=c.entryPath,i.rootpath=c.rootpath,i.rootFilename=c.rootFilename,i.relativeUrls=c.relativeUrls):(i.entryPath=g.path,i.rootpath=w.rootpath||g.path,i.rootFilename=h,i.relativeUrls=e.relativeUrls),i.relativeUrls&&(i.rootpath=e.rootpath?n(e.rootpath+o(g.path,i.entryPath)).path:g.path),e.useFileCache&&E[h])try{var j=E[h];f&&(j+="\n"+f),d(null,j,h,i,{lastModified:new Date})}catch(k){d(k,null,h)}else q(h,e.mime,function(a,b){a=F+a,E[h]=a;try{d(null,a,h,i,{lastModified:b})}catch(c){d(c,null,h)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,h)})}function s(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||F)&&(f.useFileCache=!0),r(a.href,null,function(e,h,i,j,k){if(k){k.remaining=d;var m=D&&D.getItem(i),n=D&&D.getItem(i+":timestamp");if(!c&&n&&k.lastModified&&new Date(k.lastModified).valueOf()===new Date(n).valueOf())return g(m,a),k.local=!0,b(null,null,h,a,k,i),void 0}l(i),h?(f.currentFileInfo=j,new w.Parser(f).parse(h,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,h,a,k,i)}catch(c){b(c,null,null,a)}})):b(e,null,null,a,k,i)},f,e)}function t(a,b,c){for(var d=0;dz&&(y[s]=y[s].slice(r-z),z=r)}function g(a){var b=a.charCodeAt(0);return 32===b||10===b||9===b}function h(a){var b,c;if(a instanceof Function)return a.call(A.parsers);if("string"==typeof a)b=q.charAt(r)===a?a:null,c=1,f();else{if(f(),!(b=a.exec(y[s])))return null;c=b[0].length}return b?(i(c),"string"==typeof b?b:1===b.length?b[0]:b):void 0}function i(a){for(var b=r,c=s,d=r+y[s].length,e=r+=a;d>r&&g(q.charAt(r));)r++;return y[s]=y[s].slice(a+(r-e)),z=r,0===y[s].length&&s=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function o(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:n(a,b).line+1,fileName:e}}function p(a,b){var c=m(a,b),d=n(a.index,c),e=d.line,f=d.column,g=a.call&&n(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var q,r,s,t,u,v,y,z,A,B=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var C=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files||d===B;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}};return p.prototype=new Error,p.prototype.constructor=p,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,A={imports:C,parse:function(b,d){var e,f,g,i=null;if(r=s=z=v=0,q=b.replace(/\r\n/g,"\n"),q=q.replace(/^\uFEFF/,""),A.imports.contents[a.currentFileInfo.filename]=q,y=function(b){for(var c,d,e,f,g=0,h=/(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,j=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,k=/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,l=0,m=b[0],n=0;n0?"missing closing `}`":"missing opening `{`",filename:a.currentFileInfo.filename},a)),b.map(function(a){return a.join("")})}([[]]),i)return d(new p(i,a));try{e=new x.Ruleset([],h(this.parsers.primary)),e.root=!0,e.firstRoot=!0}catch(j){return d(new p(j,a))}if(e.toCSS=function(b){return function(d,e){d=d||{};var f,g,h=new x.evalEnv(d);"object"!=typeof e||Array.isArray(e)||(e=Object.keys(e).map(function(a){var b=e[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),h.frames=[new x.Ruleset(null,e)]);try{f=b.call(this,h),(new x.joinSelectorVisitor).run(f),(new x.processExtendsVisitor).run(f),new x.toCSSVisitor({compress:Boolean(d.compress)}).run(f),d.sourceMap&&(f=new x.sourceMapOutput({writeSourceMap:d.writeSourceMap,rootNode:f,contentsMap:A.imports.contents,sourceMapFilename:d.sourceMapFilename,sourceMapURL:d.sourceMapURL,outputFilename:d.sourceMapOutputFilename,sourceMapBasepath:d.sourceMapBasepath,sourceMapRootpath:d.sourceMapRootpath,outputSourceFiles:d.outputSourceFiles,sourceMapGenerator:d.sourceMapGenerator})),g=f.toCSS({compress:Boolean(d.compress),dumpLineNumbers:a.dumpLineNumbers,strictUnits:Boolean(d.strictUnits)})}catch(i){throw new p(i,a)}if(d.cleancss&&"node"===w.mode){var j=c("clean-css");return new j({keepSpecialComments:"*",processImport:!1,noRebase:!0,noAdvanced:!0}).minify(g)}return d.compress?g.replace(/(^(\s)+)|((\s)+$)/g,""):g}}(e.eval),r57||43>b||47===b||44==b))return(a=h(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/))?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return(a=h(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/))?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=r;return"~"===q.charAt(e)&&(e++,d=!0),"`"===q.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||k("You are using JavaScript, which has been disabled."),d&&h("~"),(c=h(/^`([^`]*)`/))?new x.JavaScript(c[1],r,d):void 0):void 0}},variable:function(){var a;return"@"===q.charAt(r)&&(a=h(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e=r,f=[];if(h(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=[];;){if(d=h(/^(all)(?=\s*(\)|,))/))break;if(c=h(this.element),!c)break;b.push(c)}d=d&&d[1],f.push(new x.Extend(new x.Selector(b),d,e))}while(h(","));return j(/^\)/),a&&j(/^;/),f}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,f,g=[],i=r,k=q.charAt(r),m=!1;if("."===k||"#"===k){for(d();b=h(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);)g.push(new x.Element(c,b,r,a.currentFileInfo)),c=h(">");return h("(")&&(f=this.mixin.args.call(this,!0).args,j(")")),f=f||[],h(this.important)&&(m=!0),g.length>0&&(h(";")||l("}"))?new x.mixin.Call(g,f,i,a.currentFileInfo,m):(e(),void 0)}},args:function(a){for(var b,c,d,e,f,g,i=[],l=[],m=[],n={args:null,variadic:!1};;){if(a)g=h(this.expression);else{if(h(this.comments),"."===q.charAt(r)&&h(/^\.{3}/)){n.variadic=!0,h(";")&&!b&&(b=!0),(b?l:m).push({variadic:!0});break}g=h(this.entities.variable)||h(this.entities.literal)||h(this.entities.keyword)}if(!g)break;e=null,g.throwAwayComments&&g.throwAwayComments(),f=g;var o=null;if(a?1==g.value.length&&(o=g.value[0]):o=g,o&&o instanceof x.Variable)if(h(":"))i.length>0&&(b&&k("Cannot mix ; and , as delimiter types"),c=!0),f=j(this.expression),e=d=o.name;else{if(!a&&h(/^\.{3}/)){n.variadic=!0,h(";")&&!b&&(b=!0),(b?l:m).push({name:g.name,variadic:!0});break}a||(d=e=o.name,f=null)}f&&i.push(f),m.push({name:e,value:f}),h(",")||(h(";")||b)&&(c&&k("Cannot mix ; and , as delimiter types"),b=!0,i.length>1&&(f=new x.Value(i)),l.push({name:d,value:f}),d=null,i=[],c=!1)}return n.args=b?l:m,n},definition:function(){var a,b,c,f,g=[],i=!1;if(!("."!==q.charAt(r)&&"#"!==q.charAt(r)||l(/^[^{]*\}/))&&(d(),b=h(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=b[1];var k=this.mixin.args.call(this,!1);if(g=k.args,i=k.variadic,h(")")||(v=r,e()),h(this.comments),h(/^when/)&&(f=j(this.conditions,"expected condition")),c=h(this.block))return new x.mixin.Definition(a,g,c,f,i);e()}}},entity:function(){return h(this.entities.literal)||h(this.entities.variable)||h(this.entities.url)||h(this.entities.call)||h(this.entities.keyword)||h(this.entities.javascript)||h(this.comment)},end:function(){return h(";")||l("}")},alpha:function(){var a;if(h(/^\(opacity=/i))return(a=h(/^\d+/)||h(this.entities.variable))?(j(")"),new x.Alpha(a)):void 0},element:function(){var b,c,d;return c=h(this.combinator),b=h(/^(?:\d+\.\d+|\d+)%/)||h(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||h("*")||h("&")||h(this.attribute)||h(/^\([^()@]+\)/)||h(/^[\.#](?=@)/)||h(this.entities.variableCurly),b||h("(")&&(d=h(this.selector))&&h(")")&&(b=new x.Paren(d)),b?new x.Element(c,b,r,a.currentFileInfo):void 0},combinator:function(){var a=q.charAt(r);if(">"===a||"+"===a||"~"===a||"|"===a){for(r++;q.charAt(r).match(/\s/);)r++;return new x.Combinator(a)}return q.charAt(r-1).match(/\s/)?new x.Combinator(" "):new x.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,i=[],l=[];(b&&(e=h(this.extend))||b&&(f=h(/^when/))||(c=h(this.element)))&&(f?g=j(this.conditions,"expected condition"):g?k("CSS guard can only be used at the end of selector"):e?l.push.apply(l,e):(l.length&&k("Extend can only be used at the end of selector"),d=q.charAt(r),i.push(c),c=null),"{"!==d&&"}"!==d&&";"!==d&&","!==d&&")"!==d););return i.length>0?new x.Selector(i,l,g,r,a.currentFileInfo):(l.length&&k("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){var a,b,c;if(h("["))return(a=h(this.entities.variableCurly))||(a=j(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),(c=h(/^[|~*$^]?=/))&&(b=h(this.entities.quoted)||h(/^[0-9]+%/)||h(/^[\w-]+/)||h(this.entities.variableCurly)),j("]"),new x.Attribute(a,c,b)},block:function(){var a;return h("{")&&(a=h(this.primary))&&h("}")?a:void 0},ruleset:function(){var b,c,f,g=[];for(d(),a.dumpLineNumbers&&(f=o(r,q,a));(b=h(this.lessSelector))&&(g.push(b),h(this.comments),h(","));)b.condition&&k("Guards are only currently allowed on a single selector."),h(this.comments);if(g.length>0&&(c=h(this.block))){var i=new x.Ruleset(g,c,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=f),i}v=r,e()},rule:function(b){var c,f,g,i=q.charAt(r),j=!1;if(d(),"."!==i&&"#"!==i&&"&"!==i&&(c=h(this.variable)||h(this.ruleProperty))){if(f=b||!a.compress&&"@"!==c.charAt(0)?h(this.anonymousValue)||h(this.value):h(this.value)||h(this.anonymousValue),g=h(this.important),"+"===c[c.length-1]&&(j=!0,c=c.substr(0,c.length-1)),f&&h(this.end))return new x.Rule(c,f,g,j,u,a.currentFileInfo);if(v=r,e(),f&&!b)return this.rule(!0)}},anonymousValue:function(){var a;return(a=/^([^@+\/'"*`(;{}-]*);/.exec(y[s]))?(r+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,f=r;d();var g=h(/^@import?\s+/),i=(g?h(this.importOptions):null)||{};return g&&(b=h(this.entities.quoted)||h(this.entities.url))&&(c=h(this.mediaFeatures),h(";"))?(c=c&&new x.Value(c),new x.Import(b,c,i,f,a.currentFileInfo)):(e(),void 0)},importOptions:function(){var a,b,c,d={};if(!h("("))return null;do if(a=h(this.importOption)){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!h(","))break}while(a);return j(")"),d},importOption:function(){var a=h(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=[];do if(b=h(this.entities.keyword)||h(this.entities.variable))d.push(b);else if(h("(")){if(c=h(this.property),b=h(this.value),!h(")"))return null;if(c&&b)d.push(new x.Paren(new x.Rule(c,b,null,null,r,a.currentFileInfo,!0)));else{if(!b)return null;d.push(new x.Paren(b))}}while(b);return d.length>0?new x.Expression(d):void 0},mediaFeatures:function(){var a,b=[];do if(a=h(this.mediaFeature)){if(b.push(a),!h(","))break}else if((a=h(this.entities.variable))&&(b.push(a),!h(",")))break;while(a);return b.length>0?b:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=o(r,q,a)),h(/^@media/)&&(b=h(this.mediaFeatures),c=h(this.block))?(d=new x.Media(c,b,r,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,f,g,i,j,k,l;if("@"===q.charAt(r)){if(c=h(this["import"])||h(this.media))return c;if(d(),b=h(/^@[a-z-]+/)){switch(g=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(g="@"+b.slice(b.indexOf("-",2)+1)),g){case"@font-face":i=!0;break;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":i=!0;break;case"@host":case"@page":case"@document":case"@supports":case"@keyframes":i=!0,j=!0;break;case"@namespace":k=!0}if(j&&(l=(h(/^[^{]+/)||"").trim(),l&&(b+=" "+l)),i){if(f=h(this.block))return new x.Directive(b,f,r,a.currentFileInfo)}else if((c=k?h(this.expression):h(this.entity))&&h(";")){var m=new x.Directive(b,c,r,a.currentFileInfo);return a.dumpLineNumbers&&(m.debugInfo=o(r,q,a)),m}e()}}},value:function(){for(var a,b=[];(a=h(this.expression))&&(b.push(a),h(",")););return b.length>0?new x.Value(b):void 0},important:function(){return"!"===q.charAt(r)?h(/^! *important/):void 0},sub:function(){var a,b;return h("(")&&(a=h(this.addition))?(b=new x.Expression([a]),j(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=h(this.operand)){for(e=g(q.charAt(r-1));!l(/^\/[*\/]/)&&(c=h("/")||h("*"))&&(b=h(this.operand));)a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=g(q.charAt(r-1));return d||a}},addition:function(){var a,b,c,d,e;if(a=h(this.multiplication)){for(e=g(q.charAt(r-1));(c=h(/^[-+]\s+/)||!e&&(h("+")||h("-")))&&(b=h(this.multiplication));)a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=g(q.charAt(r-1));return d||a}},conditions:function(){var a,b,c,d=r;if(a=h(this.condition)){for(;l(/^,\s*(not\s*)?\(/)&&h(",")&&(b=h(this.condition));)c=new x.Condition("or",c||a,b,d);return c||a}},condition:function(){var a,b,c,d,e=r,f=!1;return h(/^not/)&&(f=!0),j("("),(a=h(this.addition)||h(this.entities.keyword)||h(this.entities.quoted))?((d=h(/^(?:>=|<=|=<|[<=>])/))?(b=h(this.addition)||h(this.entities.keyword)||h(this.entities.quoted))?c=new x.Condition(d,a,b,e,f):k("expected expression"):c=new x.Condition("=",a,new x.Keyword("true"),e,f),j(")"),h(/^and/)?new x.Condition("and",c,h(this.condition)):c):void 0},operand:function(){var a,b=q.charAt(r+1);"-"!==q.charAt(r)||"@"!==b&&"("!==b||(a=h("-"));var c=h(this.sub)||h(this.entities.dimension)||h(this.entities.color)||h(this.entities.variable)||h(this.entities.call);return a&&(c.parensInOp=!0,c=new x.Negative(c)),c},expression:function(){for(var a,b,c=[];a=h(this.addition)||h(this.entity);)c.push(a),!l(/^\/[\/*]/)&&(b=h("/"))&&c.push(new x.Anonymous(b));return c.length>0?new x.Expression(c):void 0},property:function(){var a;return(a=h(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/))?a[1]:void 0},ruleProperty:function(){var a;return(a=h(/^(\*?-?[_a-zA-Z0-9-]+)\s*(\+?)\s*:/))?a[1]+(a[2]||""):void 0}}}},function(d){function e(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function f(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):g(a)}function g(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function h(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var h=[a,b,c].map(function(a){return f(a,256)});return e=g(e),new d.Color(h,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?i+(f-i)*a*6:1>2*a?f:2>3*a?i+(f-i)*(2/3-a)*6:i}a=g(a)%360/360,b=h(g(b)),c=h(g(c)),d=h(g(d));var f=.5>=c?c*(b+1):c+b-c*b,i=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=g(a)%360/360*360,b=g(b),c=g(c),d=g(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var h=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],i=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*h[i[e][0]],255*h[i[e][1]],255*h[i[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=h(c.s),e(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=h(c.s),e(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=h(c.l),e(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=h(c.l),e(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=h(c.a),e(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=h(c.a),e(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=h(c.a),e(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,e(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:g(d),a.luma()*a.alphai.value)&&(k[f]=g)):(l[j]=k.length,k.push(g))):k.push(g);return 1==k.length?k[0]:(c=k.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},multiply:function(a,b){var c=a.rgb[0]*b.rgb[0]/255,d=a.rgb[1]*b.rgb[1]/255,e=a.rgb[2]*b.rgb[2]/255;return this.rgb(c,d,e)},screen:function(a,b){var c=255-(255-a.rgb[0])*(255-b.rgb[0])/255,d=255-(255-a.rgb[1])*(255-b.rgb[1])/255,e=255-(255-a.rgb[2])*(255-b.rgb[2])/255;return this.rgb(c,d,e)},overlay:function(a,b){var c=a.rgb[0]<128?2*a.rgb[0]*b.rgb[0]/255:255-2*(255-a.rgb[0])*(255-b.rgb[0])/255,d=a.rgb[1]<128?2*a.rgb[1]*b.rgb[1]/255:255-2*(255-a.rgb[1])*(255-b.rgb[1])/255,e=a.rgb[2]<128?2*a.rgb[2]*b.rgb[2]/255:255-2*(255-a.rgb[2])*(255-b.rgb[2])/255; -return this.rgb(c,d,e)},softlight:function(a,b){var c=b.rgb[0]*a.rgb[0]/255,d=c+a.rgb[0]*(255-(255-a.rgb[0])*(255-b.rgb[0])/255-c)/255;c=b.rgb[1]*a.rgb[1]/255;var e=c+a.rgb[1]*(255-(255-a.rgb[1])*(255-b.rgb[1])/255-c)/255;c=b.rgb[2]*a.rgb[2]/255;var f=c+a.rgb[2]*(255-(255-a.rgb[2])*(255-b.rgb[2])/255-c)/255;return this.rgb(d,e,f)},hardlight:function(a,b){var c=b.rgb[0]<128?2*b.rgb[0]*a.rgb[0]/255:255-2*(255-b.rgb[0])*(255-a.rgb[0])/255,d=b.rgb[1]<128?2*b.rgb[1]*a.rgb[1]/255:255-2*(255-b.rgb[1])*(255-a.rgb[1])/255,e=b.rgb[2]<128?2*b.rgb[2]*a.rgb[2]/255:255-2*(255-b.rgb[2])*(255-a.rgb[2])/255;return this.rgb(c,d,e)},difference:function(a,b){var c=Math.abs(a.rgb[0]-b.rgb[0]),d=Math.abs(a.rgb[1]-b.rgb[1]),e=Math.abs(a.rgb[2]-b.rgb[2]);return this.rgb(c,d,e)},exclusion:function(a,b){var c=a.rgb[0]+b.rgb[0]*(255-a.rgb[0]-a.rgb[0])/255,d=a.rgb[1]+b.rgb[1]*(255-a.rgb[1]-a.rgb[1])/255,e=a.rgb[2]+b.rgb[2]*(255-a.rgb[2]-a.rgb[2])/255;return this.rgb(c,d,e)},average:function(a,b){var c=(a.rgb[0]+b.rgb[0])/2,d=(a.rgb[1]+b.rgb[1])/2,e=(a.rgb[2]+b.rgb[2])/2;return this.rgb(c,d,e)},negation:function(a,b){var c=255-Math.abs(255-b.rgb[0]-a.rgb[0]),d=255-Math.abs(255-b.rgb[1]-a.rgb[1]),e=255-Math.abs(255-b.rgb[2]-a.rgb[2]);return this.rgb(c,d,e)},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q="'data:"+f+","+n+"'";return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function c(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&c();var e,f,g,h,i,j,k,l=Array.prototype.slice.call(arguments,1),m="linear",n='x="0" y="0" width="1" height="1"',o=!0,p={compress:!1},q=a.toCSS(p);switch(q){case"to bottom":e='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":e='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":e='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":e='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":m="radial",e='cx="50%" cy="50%" r="75%"',n='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(f='<'+m+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+e+">",g=0;gk?' stop-opacity="'+k+'"':"")+"/>";if(f+="',o)try{f=new Buffer(f).toString("base64")}catch(r){o=!1}return f="'data:image/svg+xml"+(o?";base64":"")+","+f+"'",new d.URL(new d.Anonymous(f))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};for(var i=[{name:"ceil"},{name:"floor"},{name:"sqrt"},{name:"abs"},{name:"tan",unit:""},{name:"sin",unit:""},{name:"cos",unit:""},{name:"atan",unit:"rad"},{name:"asin",unit:"rad"},{name:"acos",unit:"rad"}],j=function(a,b){return function(c){return null!=b&&(c=c.unify()),this._math(Math[a],b,c)}},k=0;k1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){b.add(a.compress?"{":" {\n"),a.tabLevel=(a.tabLevel||0)+1;for(var d=a.compress?"":Array(a.tabLevel+1).join(" "),e=a.compress?"":Array(a.tabLevel).join(" "),f=0;fb?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args=a.visit(this.args)},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;cf;f++)e[f]=a.operate(b,c,this.rgb[f],d.rgb[f]);return new a.Color(e,this.alpha+d.alpha)},toRGB:function(){return"#"+this.rgb.map(function(a){return a=Math.round(a),a=(a>255?255:0>a?0:a).toString(16),1===a.length?"0"+a:a}).join("")},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){var a=[Math.round(255*this.alpha)].concat(this.rgb);return"#"+a.map(function(a){return a=Math.round(a),a=(a>255?255:0>a?0:a).toString(16),1===a.length?"0"+a:a}).join("")},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(c){if(a.colors.hasOwnProperty(c))return new a.Color(a.colors[c].slice(1));if(c===b){var d=new a.Color([0,0,0],0);return d.isTransparentKeyword=!0,d}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.value,d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&this.unit.isLength())return b.add(d),void 0;c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset([],c)],this.rules[0].allowImports=!0):this.value=c,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules=a.visit(this.rules),this.value=a.visit(this.value)},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|"},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value=a.visit(this.value)},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features=a.visit(this.features),this.path=a.visit(this.path),this.options.inline||(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip)return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset([],this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}for(var h in b.frames[0].variables())e[h.slice(1)]={value:b.frames[0].variables()[h].value,toJS:function(){return this.value.eval(b).toCSS()}};try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features=a.visit(this.features),this.rules=a.visit(this.rules)},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media([],[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo);return[new a.Selector([b],null,null,this.index,this.currentFileInfo)]},markReferenced:function(){var a,b=this.rules[0].rules;for(this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c,d=this.operands[0].eval(b),e=this.operands[1].eval(b);if(b.isMathOn()){if(d instanceof a.Dimension&&e instanceof a.Color){if("*"!==this.op&&"+"!==this.op)throw{type:"Operation",message:"Can't substract or divide a color from a number"};c=e,e=d,d=c}if(!d.operate)throw{type:"Operation",message:"Operation on an invalid type"};return d.operate(b,this.op,e)}return new a.Operation(this.op,[d,e],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable="@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(b){var c=!1;"font"!==this.name||b.strictMath||(c=!0,b.strictMath=!0);try{return new a.Rule(this.name,this.value.eval(b),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}finally{c&&(b.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){if(this.paths)for(var b=0;bd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e)},genCSS:function(b,c){var d,e,f,g,h,i=[],j=[],k=!0;b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l=b.compress?"":Array(b.tabLevel+1).join(" "),m=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;d0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",0,j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b||[],this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements=a.visit(this.elements),this.extendList=a.visit(this.extendList),this.condition=a.visit(this.condition)},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,this.condition,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e},match:function(a){var b,c,d,e,f=this.elements,g=f.length;if(b=a.elements.slice(a.elements.length&&"&"===a.elements[0].value?1:0),c=b.length,d=Math.min(g,c),0===c||c>g)return 0;for(e=0;d>e;e++)if(f[e].value!==b[e].value)return 0;return d},eval:function(a){var b=this.condition&&this.condition.eval(a);return this.createDerived(this.elements.map(function(b){return b.eval(a)}),this.extendList.map(function(b){return b.eval(a)}),b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;c0)&&e.splice(0,0,b);else{b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;c0&&b.accept(this._visitor),c.visitDeeper=!1,this._mergeRules(b.rules),this._removeDuplicateRules(b.rules),b.rules.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d},_mergeRules:function(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[];for(c=0;c100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},inInheritanceChain:function(a,b){if(a===b)return!0;if(b.parents){if(this.inInheritanceChain(a,b.parents[0]))return!0;if(this.inInheritanceChain(a,b.parents[1]))return!0}return!1},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,this._sourceMapBasepath=a.sourceMapBasepath,this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a.replace(/\\/g,"/")},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename].substring(0,c);f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var c,d=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?c=this._sourceMapURL:this._sourceMapFilename&&(c=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(d):c="data:application/json,"+encodeURIComponent(d),c&&this._css.push("/*# sourceMappingURL="+c+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:z.info,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.tree.functions[A]=w.functions[A];var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={},F="";if(w.watch=function(){return w.watchMode||(w.env="development",u()),this.watchMode=!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(G){}var H=document.getElementsByTagName("link");w.sheets=[];for(var I=0;I>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof(window) === 'undefined') { - if (typeof(exports) === 'undefined') { - // Rhino - less = {}; - tree = less.tree = {}; - } else { - // Node.js - less = exports, - tree = require('less/tree'); - } -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e) } - - if (c.value && c.value[0] === '&') { - return new(tree.Element)(c, null); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) { - i += match[0].length - 1; - selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])]; - } else { - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page|@-[-a-z]+/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (typeof(window) !== 'undefined' /* browser */ || typeof(exports) === 'undefined' /* rhino */) { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('less/tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { message: "error evaluating function `" + this.name + "`", - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else if (rgb.length == 8) { - this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0; - this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - } -}; - - -})(require('less/tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Element = function (combinator, value) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value ? value.trim() : ""; -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('less/tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('less/tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('less/tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('less/tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > 1) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors( paths, context, this.selectors ); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function( paths, context, selectors ) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function( paths, context, selector ) { - var before = [], after = [], beforeElements = [], afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value[0] === '&') { - hasParentSelector = true; - } - if(!hasParentSelector) { - beforeElements.push(el); - } else { - afterElements.push(el); - } - } - - if(!hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if(beforeElements.length > 0) { - before.push(new (tree.Selector)(beforeElements)); - } - if(afterElements.length > 0) { - after.push(new (tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('less/tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - if (this.elements[0].value === other.elements[0].value) { - return true; - } else { - return false; - } -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('less/tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('less/tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('less/tree')); -require('less/tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('less/tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var sheetName = name.slice(0, name.lastIndexOf('/') + 1) + sheet.href; - var input = readFile(sheetName); - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - print("Error: " + e); - quit(1); - } - callback(root, sheet, { local: false, lastModified: 0, remaining: remaining }); - }); - - // callback({}, sheet, { local: true, remaining: remaining }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - name = args[0]; - var output = args[1]; - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - quit(1); - } else { - result = root.toCSS(); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - print("done"); -}(arguments)); diff --git a/dist/less-rhino-1.1.5.js b/dist/less-rhino-1.1.5.js deleted file mode 100644 index e755f6310..000000000 --- a/dist/less-rhino-1.1.5.js +++ /dev/null @@ -1,2481 +0,0 @@ -// -// Stub out `require` in rhino -// -function require(arg) { - return less[arg.split('/')[1]]; -}; - - -// ecma-5.js -// -// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -// -- tlrobinson Tom Robinson -// dantman Daniel Friesen - -// -// Array -// -if (!Array.isArray) { - Array.isArray = function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]" || - (obj instanceof Array); - }; -} -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(block, thisObject) { - var len = this.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - less = {}; - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'rhino'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - mime: env && env.mime, // MIME type of .less files - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - that.files[path] = root; // Store the root - - callback(root); - - if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, c, index, endIndex, k, mem; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - mem = i += length; - endIndex = i + chunks[j].length - length; - - while (i < endIndex) { - c = input.charCodeAt(i); - if (! (c === 32 || c === 10 || c === 9)) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - chunks = []; - input = str.replace(/\r\n/g, '\n'); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /[^"'`\{\}\/\(\)]+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - level = 0, - match, - chunk = chunks[0], - inParam, - inString; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = i; - - if (!inString && !inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - if (c === '{' && !inString && !inParam) { level ++; - chunk.push(c); - } else if (c === '}' && !inString && !inParam) { level --; - chunk.push(c); - chunks[++j] = chunk = []; - } else if (c === '(' && !inString && !inParam) { - chunk.push(c); - inParam = true; - } else if (c === ')' && !inString && inParam) { - chunk.push(c); - inParam = false; - } else { - if (c === '"' || c === "'" || c === '`') { - if (! inString) { - inString = c; - } else { - inString = inString === c ? false : inString; - } - } - chunk.push(c); - } - } - if (level > 0) { - throw { - type: 'Syntax', - message: "Missing closing `}`", - filename: env.filename - }; - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = []; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false }); - } catch (e) { - lines = input.split('\n'); - line = getLine(e.index); - - for (var n = e.index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - throw { - type: e.type, - message: e.message, - filename: env.filename, - index: e.index, - line: typeof(line) === 'number' ? line + 1 : null, - callLine: e.call && (getLine(e.call) + 1), - callExtract: lines[getLine(e.call)], - stack: e.stack, - column: column, - extract: [ - lines[line - 1], - lines[line], - lines[line + 1] - ] - }; - } - if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - - function getLine(index) { - return index ? (input.slice(0, index).match(/\n/g) || "").length : null; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - name: "ParseError", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function () { callback(error, root) }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { return new(tree.Keyword)(k) } - }, - - // - // 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. - // - call: function () { - var name, args, index = i; - - if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return; - - name = name[1].toLowerCase(); - - if (name === 'url') { return null } - else { i += name.length } - - if (name === 'alpha') { return $(this.alpha) } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // - // 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || ""; - if (! $(')')) throw new(Error)("missing closing ) for url()"); - - return new(tree.URL)((value.value || value.data || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - dataURI: function () { - var obj; - - if ($(/^data:/)) { - obj = {}; - obj.mime = $(/^[^\/]+\/[^,;)]+/) || ''; - obj.charset = $(/^;\s*charset=[^,;)]+/) || ''; - obj.base64 = $(/^;\s*base64/) || ''; - obj.data = $(/^,\s*[^)]+/); - - if (obj.data) { return obj } - } - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i); - - if (s !== '.' && s !== '#') { return } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - $('(') && (args = $(this.entities.arguments)) && $(')'); - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index); - } - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value; - - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) { - name = match[1]; - - while (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - params.push({ name: param.name, value: value }); - } else { - throw new(Error)("Expected value"); - } - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - if (! $(')')) throw new(Error)("missing closing ) for alpha()"); - return new(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 `*`. - // - element: function () { - var e, t, c; - - c = $(this.combinator); - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || - $('*') || $(this.attribute) || $(/^\([^)@]+\)/); - - if (e) { return new(tree.Element)(c, e, i) } - - if (c.value && c.value.charAt(0) === '&') { - return new(tree.Element)(c, null, i); - } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(c); - } else if (c === '&') { - match = '&'; - i++; - if(input.charAt(i) === ' ') { - match = '& '; - } - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)(match); - } else if (c === ':' && input.charAt(i + 1) === ':') { - i += 2; - while (input.charAt(i) === ' ') { i++ } - return new(tree.Combinator)('::'); - } else if (input.charAt(i - 1) === ' ') { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match; - save(); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - return new(tree.Ruleset)(selectors, rules); - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path; - if ($(/^@import\s+/) && - (path = $(this.entities.quoted) || $(this.entities.url)) && - $(';')) { - return new(tree.Import)(path, imports); - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, types; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import'])) { - return value; - } else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-|-moz-)?keyframes/)) { - types = ($(/^[^{]+/) || '').trim(); - if (rules = $(this.block)) { - return new(tree.Directive)(name + " " + types, rules); - } - } else if (name = $(/^@[-a-z]+/)) { - if (name === '@font-face') { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while ((op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (path.charAt(0) !== '/' && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math.round(number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math.round(n); - } else { - throw { - error: "RuntimeError", - message: "math functions take numbers as parameters" - }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index) { - this.name = name; - this.args = args; - this.index = index; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { message: "error evaluating function `" + this.name + "`", - index: this.index }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - env.frames.unshift(this); - this.ruleset = this.ruleset && this.ruleset.eval(env); - env.frames.shift(); - return this; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - this.value = value ? value.trim() : ""; - this.index = index; -}; -tree.Element.prototype.toCSS = function (env) { - return this.combinator.toCSS(env || {}) + this.value; -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else if (value === '& ') { - this.value = '& '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - '&' : '', - '& ' : ' ', - ':' : ' :', - '::': '::', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports) { - var that = this; - - this._path = path; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (root) { - if (! root) { - throw new(Error)("Error parsing " + that.path); - } - that.root = root; - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function () { - if (this.css) { - return "@import " + this._path.toCSS() + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { return a.eval(env) }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments).rules); - match = true; - } catch (e) { - throw { message: e.message, index: e.index, stack: e.stack, call: this.index }; - } - } - } - if (match) { - return rules; - } else { - throw { message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index }; - } - } - } - throw { message: this.selector.toCSS().trim() + " is undefined", - index: this.index }; - } -}; - -tree.mixin.Definition = function (name, params, rules) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; - - for (var i = 0, val; i < this.params.length; i++) { - if (this.params[i].name) { - if (val = (args && args[i]) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env))); - } else { - throw { message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push(args[i] || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - return new(tree.Ruleset)(null, this.rules.slice(0)).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (argsLength < this.required) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return v.value || v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + ";"; - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0)); - - ruleset.root = this.root; - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - Array.prototype.splice - .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - rule; - - if (! this.root) { - if (context.length === 0) { - paths = this.selectors.map(function (s) { return [s] }); - } else { - this.joinSelectors( paths, context, this.selectors ); - } - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', ')); - css.push(selector, - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - var before = [], after = [], beforeElements = [], - afterElements = [], hasParentSelector = false, el; - - for (var i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.combinator.value.charAt(0) === '&') { - hasParentSelector = true; - } - if (hasParentSelector) afterElements.push(el); - else beforeElements.push(el); - } - - if (! hasParentSelector) { - afterElements = beforeElements; - beforeElements = []; - } - - if (beforeElements.length > 0) { - before.push(new(tree.Selector)(beforeElements)); - } - - if (afterElements.length > 0) { - after.push(new(tree.Selector)(afterElements)); - } - - for (var c = 0; c < context.length; c++) { - paths.push(before.concat(context[c]).concat(after)); - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; - if (this.elements[0].combinator.value === "") { - this.elements[0].combinator.value = ' '; - } -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - return this._css = this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - if (val.data) { - this.attrs = val; - } else { - // Add the base path if the URL is relative and we are in the browser - if (!/^(?:https?:\/\/|file:\/\/|data:)?/.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') { - val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - this.value = val; - this.paths = paths; - } -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data - : this.value.toCSS()) + ")"; - }, - eval: function (ctx) { - return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index) { this.name = name, this.index = index }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { message: "variable " + name + " is undefined", - index: this.index }; - } - } -}; - -})(require('../tree')); -require('./tree').find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -require('./tree').jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var sheetName = name.slice(0, name.lastIndexOf('/') + 1) + sheet.href; - var input = readFile(sheetName); - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - print("Error: " + e); - quit(1); - } - callback(root, sheet, { local: false, lastModified: 0, remaining: remaining }); - }); - - // callback({}, sheet, { local: true, remaining: remaining }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - name = args[0]; - var output = args[1]; - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - quit(1); - } else { - result = root.toCSS(); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - print("done"); -}(arguments)); diff --git a/dist/less-rhino-1.3.1.js b/dist/less-rhino-1.3.1.js deleted file mode 100644 index 1ebe0ea4b..000000000 --- a/dist/less-rhino-1.3.1.js +++ /dev/null @@ -1,3725 +0,0 @@ -// -// Stub out `require` in rhino -// -function require(arg) { - return less[arg.split('/')[1]]; -}; - - -// ecma-5.js -// -// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -// -- tlrobinson Tom Robinson -// dantman Daniel Friesen - -// -// Array -// -if (!Array.isArray) { - Array.isArray = function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]" || - (obj instanceof Array); - }; -} -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(block, thisObject) { - var len = this.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - if (!env.contents) { env.contents={}; } // env.contents must be passed arround with top env - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env && env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: {}, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env && env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = path in that.files; - - that.files[path] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(e) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - throw { index: i, type: type || 'Syntax', message: msg }; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length; i++) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - c = input.charAt(i); - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - } - if (level > 0) { - error = new(LessError)({ - index: i, - type: 'Parse', - message: "missing closing `}`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('./cssmin').compressor.cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), imports.paths); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - if ((c > 57 || c < 45) || c === 47) return; - - if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args = [], arg, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - while (arg = $(this.expression)) { - value = arg; - name = null; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (value = $(this.expression)) { - name = val.name; - } else { - throw new(Error)("Expected value"); - } - } - } - } - - args.push({ name: name, value: value }); - - if (! $(',')) { break } - } - if (! $(')')) throw new(Error)("Expected )"); - } - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*(;|})/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^)@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(') && (v = ($(this.entities.variableCurly) || $(this.entities.variable))) && $(')')) { - e = new(tree.Paren)(v); - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - // depreciated, will be removed soon - if ($('(')) { - sel = $(this.entity); - expect(')'); - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - tag: function () { - return $(/^[A-Za-z][A-Za-z-]*[0-9]?/) || $('*'); - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = $(this.entity)) && $(';')) { - return new(tree.Directive)(name, value); - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - // __ Now using the hack of passing a ref to top parser's content cache in the 1st arg. __ - loadStyleSheet({ href: path, title: path, type: env.mime, contents: env.contents }, function (e) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.apply(null, arguments); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return number(c) }), - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) - * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(number(n).toFixed(fraction), n.unit); - } else if (typeof(n) === 'number') { - return n.toFixed(fraction); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - ceil: function (n) { - return this._math('ceil', n); - }, - floor: function (n) { - return this._math('floor', n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(Math[fn](number(n)), n.unit); - } else if (typeof(n) === 'number') { - return Math[fn](n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(hsla) { - return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a); -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - // TODO: Perform unit conversion before comparing - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css(\?.*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - Array.prototype - .splice - .apply(ruleset.rules, - [i, 1].concat(ruleset.rules[i].eval(env))); - } - } - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var blockIndex = env.mediaBlocks.length; - env.mediaPath.push(this); - env.mediaBlocks.push(this); - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaBlocks[blockIndex] = media; - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, args, rules = [], match = false; - - for (var i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - for (var m = 0; m < mixins.length; m++) { - if (mixins[m].match(args, env)) { - try { - Array.prototype.push.apply( - rules, mixins[m].eval(env, this.arguments, this.important).rules); - match = true; - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - } - if (match) { - return rules; - } else { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - this.arguments.map(function (a) { - return a.toCSS(); - }).join(', ') + ")`", - index: this.index, filename: this.filename }; - } - } - } - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, args) { - var frame = new(tree.Ruleset)(null, []), varargs, arg; - - for (var i = 0, val, name; i < this.params.length; i++) { - arg = args && args[i] - - if (arg && arg.name) { - frame.rules.unshift(new(tree.Rule)(arg.name, arg.value.eval(env))); - args.splice(i, 1); - i--; - continue; - } - - if (name = this.params[i].name) { - if (this.params[i].variadic && args) { - varargs = []; - for (var j = i; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else if (val = (arg && arg.value) || this.params[i].value) { - frame.rules.unshift(new(tree.Rule)(name, val.eval(env))); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - } - } - return frame; - }, - eval: function (env, args, important) { - var frame = this.evalParams(env, args), context, _arguments = [], rules, start; - - for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { - _arguments.push((args[i] && args[i].value) || this.params[i].value); - } - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.rules.map(function (r) { - return new(tree.Rule)(r.name, r.value, '!important', r.index); - }) : this.rules.slice(0); - - return new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(this.frames, env.frames) - }); - }, - match: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, args)].concat(env.frames) - })) { return false } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return ('value' in v) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules = []; - - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.Import) { - rules = rules.concat(ruleset.rules[i].eval(env)); - } else { - rules.push(ruleset.rules[i]); - } - } - ruleset.rules = rules; - rules = []; - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = rules.concat(ruleset.rules[i].eval(env)); - } else { - rules.push(ruleset.rules[i]); - } - } - ruleset.rules = rules; - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - match: function (args) { - return !args || args.length === 0; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Directive) || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; -}; -tree.Selector.prototype.match = function (other) { - var len = this.elements.length, - olen = other.elements.length, - max = Math.min(len, olen); - - if (len < olen) { - return false; - } else { - for (var i = 0; i < max; i++) { - if (this.elements[i].value !== other.elements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, paths) { - this.value = val; - this.paths = paths; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx); - - // Add the base path if the URL is relative and we are in the browser - if (typeof window !== 'undefined' && typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value) && this.paths.length > 0) { - val.value = this.paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value); - } - - return new(tree.URL)(val, this.paths); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { return variable } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:"' + ctx.debugInfo.fileName + '";}line{font-family:"' + ctx.debugInfo.lineNumber + '";}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return error(e, sheetName); - } - try { - callback(e, root, sheet, { local: false, lastModified: 0, remaining: remaining }); - } catch(e) { - error(e, sheetName); - } - }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - var output, - compress = false, - i; - - for(i = 0; i < args.length; i++) { - switch(args[i]) { - case "-x": - compress = true; - break; - default: - if (!name) { - name = args[i]; - } else if (!output) { - output = args[i]; - } else { - print("unrecognised parameters"); - print("input_file [output_file] [-x]"); - } - } - } - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - try { - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - error(e, name); - quit(1); - } else { - result = root.toCSS({compress: compress || false}); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - error(e, name); - quit(1); - } - print("done"); -}(arguments)); - -function error(e, filename) { - - var content = "Error : " + filename + "\n"; - - filename = e.filename || filename; - - if (e.message) { - content += e.message + "\n"; - } - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - content += - String(parseInt(e.line) + (i - 1)) + - ":" + e.extract[i] + "\n"; - } - }; - - if (e.stack) { - content += e.stack; - } else if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n'; - errorline(e, 0); - errorline(e, 1); - errorline(e, 2); - } - print(content); -} \ No newline at end of file diff --git a/dist/less-rhino-1.3.2.js b/dist/less-rhino-1.3.2.js deleted file mode 100644 index 4b83596f7..000000000 --- a/dist/less-rhino-1.3.2.js +++ /dev/null @@ -1,3990 +0,0 @@ -// -// Stub out `require` in rhino -// -function require(arg) { - return less[arg.split('/')[1]]; -}; - - -// ecma-5.js -// -// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -// -- tlrobinson Tom Robinson -// dantman Daniel Friesen - -// -// Array -// -if (!Array.isArray) { - Array.isArray = function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]" || - (obj instanceof Array); - }; -} -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(block, thisObject) { - var len = this.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree, charset; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - // env.contents and files must be passed arround with top env - if (!env.contents) { env.contents = {}; } - env.rootpath = env.rootpath || ''; // env.rootpath must be initialized to '' if not provided - if (!env.files) { env.files = {}; } - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, fullPath) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = fullPath in that.files; - - that.files[fullPath] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(that.error) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error, env); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - e = error || e; - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.rootpath); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, argsSemiColon = [], argsComma = [], args, delim, arg, nameLoop, expressions, isSemiColonSeperated, expressionContainsNamed, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - expressions = []; - while (arg = $(this.expression)) { - nameLoop = null; - value = arg; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } - } - } - - expressions.push(value); - - argsComma.push({ name: nameLoop, value: value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name: name, value: value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - expect(')'); - } - - args = isSemiColonSeperated ? argsSemiColon : argsComma; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - params.push({ variadic: true }); - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',') || $(';')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.entities.variableCurly) || - $(this.entities.variable) || - $(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - // depreciated, will be removed soon - if ($('(')) { - sel = $(this.entity); - expect(')'); - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ - href: path, - title: path, - type: env.mime, - contents: env.contents, - files: env.files, - rootpath: env.rootpath, - entryPath: env.entryPath, - relativeUrls: env.relativeUrls }, - function (e, root, data, sheet, _, path) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.call(null, e, root, path); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) * - color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, n); - }, - ceil: function (n) { - return this._math(Math.ceil, n); - }, - floor: function (n) { - return this._math(Math.floor, n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit == '%') { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }); - - if (this.name in tree.functions) { // 1. - try { - return tree.functions[this.name].apply(tree.functions, args); - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } else { // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - } - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - if (other.unit && this.unit !== other.unit) { - return -1; - } - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index, rootpath) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - this.rootpath = rootpath; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /(\.[a-z]*$)|([\?;].*)$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css([\?;].*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - // Add the base path if the import is relative - if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) { - this._path.value = this.rootpath + this._path.value; - } - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex; - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(mixinFrames) - }); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames) - })) { return false } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { name: "OperationError", - message: "Operation on an invalid type" }; - } - - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Rule.prototype.makeImportant = function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(paths, env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; -}; -tree.Selector.prototype.match = function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen) - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, rootpath) { - this.value = val; - this.rootpath = rootpath; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) { - rootpath = this.rootpath; - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, this.rootpath); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return error(e, sheetName); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - error(e, sheetName); - } - }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - var output, - compress = false, - i; - - for(i = 0; i < args.length; i++) { - switch(args[i]) { - case "-x": - compress = true; - break; - default: - if (!name) { - name = args[i]; - } else if (!output) { - output = args[i]; - } else { - print("unrecognised parameters"); - print("input_file [output_file] [-x]"); - } - } - } - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - try { - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - error(e, name); - quit(1); - } else { - result = root.toCSS({compress: compress || false}); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - error(e, name); - quit(1); - } - print("done"); -}(arguments)); - -function error(e, filename) { - - var content = "Error : " + filename + "\n"; - - filename = e.filename || filename; - - if (e.message) { - content += e.message + "\n"; - } - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - content += - String(parseInt(e.line) + (i - 1)) + - ":" + e.extract[i] + "\n"; - } - }; - - if (e.stack) { - content += e.stack; - } else if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n'; - errorline(e, 0); - errorline(e, 1); - errorline(e, 2); - } - print(content); -} \ No newline at end of file diff --git a/dist/less-rhino-1.3.3.js b/dist/less-rhino-1.3.3.js deleted file mode 100644 index 857a97c94..000000000 --- a/dist/less-rhino-1.3.3.js +++ /dev/null @@ -1,4002 +0,0 @@ -// -// Stub out `require` in rhino -// -function require(arg) { - return less[arg.split('/')[1]]; -}; - - -// ecma-5.js -// -// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -// -- tlrobinson Tom Robinson -// dantman Daniel Friesen - -// -// Array -// -if (!Array.isArray) { - Array.isArray = function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]" || - (obj instanceof Array); - }; -} -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(block, thisObject) { - var len = this.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree, charset; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - // env.contents and files must be passed arround with top env - if (!env.contents) { env.contents = {}; } - env.rootpath = env.rootpath || ''; // env.rootpath must be initialized to '' if not provided - if (!env.files) { env.files = {}; } - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, fullPath) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = fullPath in that.files; - - that.files[fullPath] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(that.error) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error, env); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - e = error || e; - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.rootpath); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, argsSemiColon = [], argsComma = [], args, delim, arg, nameLoop, expressions, isSemiColonSeperated, expressionContainsNamed, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - expressions = []; - while (arg = $(this.expression)) { - nameLoop = null; - value = arg; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } - } - } - - expressions.push(value); - - argsComma.push({ name: nameLoop, value: value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name: name, value: value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - expect(')'); - } - - args = isSemiColonSeperated ? argsSemiColon : argsComma; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - params.push({ variadic: true }); - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',') || $(';')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.entities.variableCurly) || - $(this.entities.variable) || - $(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match; - - // depreciated, will be removed soon - if ($('(')) { - sel = $(this.entity); - if (!$(')')) { return null; } - return new(tree.Selector)([new(tree.Element)('', sel, i)]); - } - - while (e = $(this.element)) { - c = input.charAt(i); - elements.push(e) - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements) } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ - href: path, - title: path, - type: env.mime, - contents: env.contents, - files: env.files, - rootpath: env.rootpath, - entryPath: env.entryPath, - relativeUrls: env.relativeUrls }, - function (e, root, data, sheet, _, path) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.call(null, e, root, path); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) * - color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, n); - }, - ceil: function (n) { - return this._math(Math.ceil, n); - }, - floor: function (n) { - return this._math(Math.floor, n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit == '%') { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit == '%' ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // When evaluating a function call, - // we either find the function in `tree.functions` [1], - // in which case we call it, passing the evaluated arguments, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }), - result; - - if (this.name in tree.functions) { // 1. - try { - result = tree.functions[this.name].apply(tree.functions, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = unit || null; -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - var css = this.value + this.unit; - return css; - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - // In the future, we could implement some unit - // conversions such that `100cm + 10mm` would yield - // `101cm`. - operate: function (op, other) { - return new(tree.Dimension) - (tree.operate(op, this.value, other.value), - this.unit || other.unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - if (other.value > this.value) { - return -1; - } else if (other.value < this.value) { - return 1; - } else { - if (other.unit && this.unit !== other.unit) { - return -1; - } - return 0; - } - } else { - return -1; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index, rootpath) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - this.rootpath = rootpath; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /(\.[a-z]*$)|([\?;].*)$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css([\?;].*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - // Add the base path if the import is relative - if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) { - this._path.value = this.rootpath + this._path.value; - } - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex; - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(mixinFrames) - }); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames) - })) { return false } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { name: "OperationError", - message: "Operation on an invalid type" }; - } - - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env) + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Rule.prototype.makeImportant = function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (this._rulesets) { return this._rulesets } - else { - return this._rulesets = this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - } - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(paths, env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements) { - this.elements = elements; -}; -tree.Selector.prototype.match = function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen) - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - })); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, rootpath) { - this.value = val; - this.rootpath = rootpath; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) { - rootpath = this.rootpath; - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, this.rootpath); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return error(e, sheetName); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - error(e, sheetName); - } - }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - var output, - compress = false, - i; - - for(i = 0; i < args.length; i++) { - switch(args[i]) { - case "-x": - compress = true; - break; - default: - if (!name) { - name = args[i]; - } else if (!output) { - output = args[i]; - } else { - print("unrecognised parameters"); - print("input_file [output_file] [-x]"); - } - } - } - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - try { - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - error(e, name); - quit(1); - } else { - result = root.toCSS({compress: compress || false}); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - error(e, name); - quit(1); - } - print("done"); -}(arguments)); - -function error(e, filename) { - - var content = "Error : " + filename + "\n"; - - filename = e.filename || filename; - - if (e.message) { - content += e.message + "\n"; - } - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - content += - String(parseInt(e.line) + (i - 1)) + - ":" + e.extract[i] + "\n"; - } - }; - - if (e.stack) { - content += e.stack; - } else if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n'; - errorline(e, 0); - errorline(e, 1); - errorline(e, 2); - } - print(content); -} \ No newline at end of file diff --git a/dist/less-rhino-1.4.0.js b/dist/less-rhino-1.4.0.js deleted file mode 100644 index a90573f7f..000000000 --- a/dist/less-rhino-1.4.0.js +++ /dev/null @@ -1,4273 +0,0 @@ -// -// Stub out `require` in rhino -// -function require(arg) { - return less[arg.split('/')[1]]; -}; - - -// ecma-5.js -// -// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License -// -- tlrobinson Tom Robinson -// dantman Daniel Friesen - -// -// Array -// -if (!Array.isArray) { - Array.isArray = function(obj) { - return Object.prototype.toString.call(obj) === "[object Array]" || - (obj instanceof Array); - }; -} -if (!Array.prototype.forEach) { - Array.prototype.forEach = function(block, thisObject) { - var len = this.length >>> 0; - for (var i = 0; i < len; i++) { - if (i in this) { - block.call(thisObject, this[i], i, this); - } - } - }; -} -if (!Array.prototype.map) { - Array.prototype.map = function(fun /*, thisp*/) { - var len = this.length >>> 0; - var res = new Array(len); - var thisp = arguments[1]; - - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} -if (!Array.prototype.filter) { - Array.prototype.filter = function (block /*, thisp */) { - var values = []; - var thisp = arguments[1]; - for (var i = 0; i < this.length; i++) { - if (block.call(thisp, this[i])) { - values.push(this[i]); - } - } - return values; - }; -} -if (!Array.prototype.reduce) { - Array.prototype.reduce = function(fun /*, initial*/) { - var len = this.length >>> 0; - var i = 0; - - // no value to return if no initial value and an empty array - if (len === 0 && arguments.length === 1) throw new TypeError(); - - if (arguments.length >= 2) { - var rv = arguments[1]; - } else { - do { - if (i in this) { - rv = this[i++]; - break; - } - // if array contains no values, no initial value to return - if (++i >= len) throw new TypeError(); - } while (true); - } - for (; i < len; i++) { - if (i in this) { - rv = fun.call(null, rv, this[i], i, this); - } - } - return rv; - }; -} -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (value /*, fromIndex */ ) { - var length = this.length; - var i = arguments[1] || 0; - - if (!length) return -1; - if (i >= length) return -1; - if (i < 0) i += length; - - for (; i < length; i++) { - if (!Object.prototype.hasOwnProperty.call(this, i)) { continue } - if (value === this[i]) return i; - } - return -1; - }; -} - -// -// Object -// -if (!Object.keys) { - Object.keys = function (object) { - var keys = []; - for (var name in object) { - if (Object.prototype.hasOwnProperty.call(object, name)) { - keys.push(name); - } - } - return keys; - }; -} - -// -// String -// -if (!String.prototype.trim) { - String.prototype.trim = function () { - return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); - }; -} -var less, tree, charset; - -if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") { - // Rhino - // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88 - if (typeof(window) === 'undefined') { less = {} } - else { less = window.less = {} } - tree = less.tree = {}; - less.mode = 'rhino'; -} else if (typeof(window) === 'undefined') { - // Node.js - less = exports, - tree = require('./tree'); - less.mode = 'node'; -} else { - // Browser - if (typeof(window.less) === 'undefined') { window.less = {} } - less = window.less, - tree = window.less.tree = {}; - less.mode = 'browser'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser; - - var that = this; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed arround by reference. - var env = env || { }; - // env.contents and files must be passed arround with top env - if (!env.contents) { env.contents = {}; } - env.rootpath = env.rootpath || ''; // env.rootpath must be initialized to '' if not provided - if (!env.files) { env.files = {}; } - - // This function is called after all files - // have been imported through `@import`. - var finish = function () {}; - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, callback) { - var that = this; - this.queue.push(path); - - // - // Import a file asynchronously - // - less.Parser.importer(path, this.paths, function (e, root, fullPath) { - that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue - - var imported = fullPath in that.files; - - that.files[fullPath] = root; // Store the root - - if (e && !that.error) { that.error = e } - - callback(e, root, imported); - - if (that.queue.length === 0) { finish(that.error) } // Call `finish` if we're done importing - }, env); - } - }; - - function save() { temp = chunks[j], memo = i, current = i } - function restore() { chunks[j] = temp, i = memo, current = i } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, args, length, index, k; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++ } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - if (tok.test(chunks[j])) { - return true; - } else { - return false; - } - } - } - - function getInput(e, env) { - if (e.filename && env.filename && (e.filename !== env.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, input) { - for (var n = index, column = -1; - n >= 0 && input.charAt(n) !== '\n'; - n--) { column++ } - - return { line: typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null, - column: column }; - } - - function getFileName(e) { - if(less.mode === 'browser' || less.mode === 'rhino') - return e.filename; - else - return require('path').resolve(e.filename); - } - - function getDebugInfo(index, inputStream, e) { - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: getFileName(e) - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = e.call && (getLocation(e.call, input).line + 1); - this.callExtract = lines[getLocation(e.call, input).line]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - this.env.filename = this.env.filename || null; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, start, end, zone, line, lines, buff = [], c, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': if (! inParam) { level ++; chunk.push(c); break } - case '}': if (! inParam) { level --; chunk.push(c); chunks[++j] = chunk = []; break } - case '(': if (! inParam) { inParam = true; chunk.push(c); break } - case ')': if ( inParam) { inParam = false; chunk.push(c); break } - default: chunk.push(c); - } - - i++; - } - if (level != 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.filename - }, env); - } - - return chunks.map(function (c) { return c.join('') });; - })([[]]); - - if (error) { - return callback(error, env); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - var line, lines, column; - - return function (options, variables) { - var frames = [], importError; - - options = options || {}; - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, 0); - }); - frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var css = evaluate.call(this, { frames: frames }) - .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers }); - } catch (e) { - throw new(LessError)(e, env); - } - - if ((importError = parser.imports.error)) { // Check if there was an error during importing - if (importError instanceof LessError) throw importError; - else throw new(LessError)(importError, env); - } - - if (options.yuicompress && less.mode === 'node') { - return require('ycssmin').cssmin(css); - } else if (options.compress) { - return css.replace(/(\s)+/g, "$1"); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - lines = input.split('\n'); - line = (input.slice(0, i).match(/\n/g) || "").length + 1; - - for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ } - - error = { - type: "Parse", - message: "Syntax Error on line " + line, - index: i, - filename: env.filename, - line: line, - column: column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - if (this.imports.queue.length > 0) { - finish = function (e) { - e = error || e; - if (e) callback(e); - else callback(null, root); - }; - } else { - callback(error, root); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') return; - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment); - } - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return; - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - if (tree.colors.hasOwnProperty(k)) { - // detect named color - return new(tree.Color)(tree.colors[k].slice(1)); - } else { - return new(tree.Keyword)(k); - } - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return; - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null } - else { i += name.length } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) return; - - if (name) { return new(tree.Call)(name, args, index, env.filename) } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { break } - } - return args; - }, - literal: function () { - return $(this.entities.ratio) || - $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) return; - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.rootpath); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.filename); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var name, curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.filename); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) return; - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A Ratio - // - // 16/9 - // - ratio: function () { - var value, c = input.charCodeAt(i); - if (c > 57 || c < 48) return; - - if (value = $(/^(\d+\/\d+)/)) { - return new(tree.Ratio)(value[1]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++, e = true } // Escaped strings - if (input.charAt(j) !== '`') { return } - - e && $('~'); - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] } - }, - - // - // A font size/line-height shorthand - // - // small/12px - // - // We need to peek first, or we'll match on keywords and dimensions - // - shorthand: function () { - var a, b; - - if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return; - - save(); - - if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) { - return new(tree.Shorthand)(a, b); - } - - restore(); - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements = [], e, args, index = i; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) { - elements.push(new(tree.Element)(null, e, i)); - } - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return new(tree.Extend)(elements, index); - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, argsSemiColon = [], argsComma = [], args, delim, arg, nameLoop, expressions, isSemiColonSeperated, expressionContainsNamed, index = i, s = input.charAt(i), name, value, important = false; - - if (s !== '.' && s !== '#') { return } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i)); - c = $('>'); - } - if ($('(')) { - expressions = []; - while (arg = $(this.expression)) { - nameLoop = null; - value = arg; - - // Variable - if (arg.value.length == 1) { - var val = arg.value[0]; - if (val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } - } - } - - expressions.push(value); - - argsComma.push({ name: nameLoop, value: value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name: name, value: value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - expect(')'); - } - - args = isSemiColonSeperated ? argsSemiColon : argsComma; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.filename, important); - } - - restore(); - }, - - // - // 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. - // - definition: function () { - var name, params = [], match, ruleset, param, value, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) return; - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - do { - $(this.comment); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - variadic = true; - params.push({ variadic: true }); - break; - } else if (param = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword)) { - // Variable - if (param instanceof tree.Variable) { - if ($(':')) { - value = expect(this.expression, 'expected expression'); - params.push({ name: param.name, value: value }); - } else if ($(/^\.{3}/)) { - params.push({ name: param.name, variadic: true }); - variadic = true; - break; - } else { - params.push({ name: param.name }); - } - } else { - params.push({ value: param }); - } - } else { - break; - } - } while ($(',') || $(';')) - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comment); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) return; - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, t, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = (//$(this.entities.variableCurly) || - $(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i) } - }, - - // - // 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var match, c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++ } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function () { - var sel, e, elements = [], c, match, extend; - - while ((extend = $(this.extend)) || (e = $(this.element))) { - if (!e) { - break; - } - c = input.charAt(i); - elements.push(e) - e = null; - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { break } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extend) } - if (extend) { error("Extend must be used to extend a selector"); } - }, - attribute: function () { - var attr = '', key, val, op; - - if (! $('[')) return; - - if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) { - if ((op = $(/^[|~*$^]?=/)) && - (val = $(this.entities.quoted) || $(/^[\w-]+/))) { - attr = [key, op, val.toCSS ? val.toCSS() : val].join(''); - } else { attr = key } - } - - if (! $(']')) return; - - if (attr) { return "[" + attr + "]" } - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, match, debugInfo; - - save(); - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - while (s = $(this.selector)) { - selectors.push(s); - $(this.comment); - if (! $(',')) { break } - $(this.comment); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) - ruleset.debugInfo = debugInfo; - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function () { - var name, value, c = input.charAt(i), important, match; - save(); - - if (c === '.' || c === '#' || c === '&') { return } - - if (name = $(this.variable) || $(this.property)) { - if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) { - i += match[0].length - 1; - value = new(tree.Anonymous)(match[1]); - } else if (name === "font") { - value = $(this.font); - } else { - value = $(this.value); - } - important = $(this.important); - - if (value && $(this.end)) { - return new(tree.Rule)(name, value, important, memo); - } else { - furthest = i; - restore(); - } - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import(?:-(once|multiple))?\s+/); - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - var importOnce = dir[1] !== 'multiple'; - return new(tree.Import)(path, imports, features, importOnce, index, env.rootpath); - } - } - - restore(); - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = $(this.entities.keyword)) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.entity); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) - debugInfo = getDebugInfo(i, input, env); - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features); - if(env.dumpLineNumbers) - media.debugInfo = debugInfo; - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, identifier, e, nodes, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression; - - if (input.charAt(i) !== '@') return; - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) return; - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - name += " " + ($(/^[^{]+/) || '').trim(); - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - restore(); - }, - font: function () { - var value = [], expression = [], weight, shorthand, font, e; - - while (e = $(this.shorthand) || $(this.entity)) { - expression.push(e); - } - value.push(new(tree.Expression)(expression)); - - if ($(',')) { - while (e = $(this.expression)) { - value.push(e); - if (! $(',')) { break } - } - } - return new(tree.Value)(value); - }, - - // - // 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 `;`. - // - value: function () { - var e, expressions = [], important; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var e; - - if ($('(') && (e = $(this.expression)) && $(')')) { - return e; - } - }, - multiplication: function () { - var m, a, op, operation; - if (m = $(this.operand)) { - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation; - if (m = $(this.multiplication)) { - while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - operation = new(tree.Operation)(op, [operation || m, a]); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while ($(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o]) - : o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = [], d; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) { - return name[1]; - } - } - } - }; -}; - -if (less.mode === 'browser' || less.mode === 'rhino') { - // - // Used by `@import` directives - // - less.Parser.importer = function (path, paths, callback, env) { - if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) { - path = paths[0] + path; - } - // We pass `true` as 3rd argument, to force the reload of the import. - // This is so we can get the syntax tree as opposed to just the CSS output, - // as we need this to evaluate the current stylesheet. - loadStyleSheet({ - href: path, - title: path, - type: env.mime, - contents: env.contents, - files: env.files, - rootpath: env.rootpath, - entryPath: env.entryPath, - relativeUrls: env.relativeUrls }, - function (e, root, data, sheet, _, path) { - if (e && typeof(env.errback) === "function") { - env.errback.call(null, path, paths, callback, env); - } else { - callback.call(null, e, root, path); - } - }, true); - }; -} - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 256); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - h = (number(h) % 360) / 360; - s = number(s); l = number(l); a = number(a); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - - function hue(h) { - 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; - } - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) + - 0.7152 * (color.rgb[1]/255) + - 0.0722 * (color.rgb[2]/255)) * - color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = threshold.value; - } - if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, n); - }, - ceil: function (n) { - return this._math(Math.ceil, n); - }, - floor: function (n) { - return this._math(Math.floor, n); - }, - _math: function (fn, n) { - if (n instanceof tree.Dimension) { - return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - return new(tree.Color)(n.value.slice(1)); - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return (n instanceof tree.Dimension) && n.unit.is('px') ? tree.True : tree.False; - }, - ispercentage: function (n) { - return (n instanceof tree.Dimension) && n.unit.is('%') ? tree.True : tree.False; - }, - isem: function (n) { - return (n instanceof tree.Dimension) && n.unit.is('em') ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - - /* Blending modes */ - - multiply: function(color1, color2) { - var r = color1.rgb[0] * color2.rgb[0] / 255; - var g = color1.rgb[1] * color2.rgb[1] / 255; - var b = color1.rgb[2] * color2.rgb[2] / 255; - return this.rgb(r, g, b); - }, - screen: function(color1, color2) { - var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - overlay: function(color1, color2) { - var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255; - var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255; - var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - softlight: function(color1, color2) { - var t = color2.rgb[0] * color1.rgb[0] / 255; - var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255; - t = color2.rgb[1] * color1.rgb[1] / 255; - var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255; - t = color2.rgb[2] * color1.rgb[2] / 255; - var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255; - return this.rgb(r, g, b); - }, - hardlight: function(color1, color2) { - var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255; - var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255; - var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - difference: function(color1, color2) { - var r = Math.abs(color1.rgb[0] - color2.rgb[0]); - var g = Math.abs(color1.rgb[1] - color2.rgb[1]); - var b = Math.abs(color1.rgb[2] - color2.rgb[2]); - return this.rgb(r, g, b); - }, - exclusion: function(color1, color2) { - var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255; - var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255; - var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255; - return this.rgb(r, g, b); - }, - average: function(color1, color2) { - var r = (color1.rgb[0] + color2.rgb[0]) / 2; - var g = (color1.rgb[1] + color2.rgb[1]) / 2; - var b = (color1.rgb[2] + color2.rgb[2]) / 2; - return this.rgb(r, g, b); - }, - negation: function(color1, color2) { - var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]); - var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]); - var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]); - return this.rgb(r, g, b); - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - } -}; - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -})(require('./tree')); -(function (tree) { - tree.colors = { - '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', - // 'transparent':'rgba(0,0,0,0)', - 'turquoise':'#40e0d0', - 'violet':'#ee82ee', - 'wheat':'#f5deb3', - 'white':'#ffffff', - 'whitesmoke':'#f5f5f5', - 'yellow':'#ffff00', - 'yellowgreen':'#9acd32' - }; -})(require('./tree')); -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - toCSS: function () { - return "alpha(opacity=" + - (this.value.toCSS ? this.value.toCSS() : this.value) + ")"; - }, - eval: function (env) { - if (this.value.eval) { this.value = this.value.eval(env) } - return this; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Anonymous = function (string) { - this.value = string.value || string; -}; -tree.Anonymous.prototype = { - toCSS: function () { - return this.value; - }, - eval: function () { return this }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - toCSS: function () { - return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - } -}; - -})(require('../tree'));(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, filename) { - this.name = name; - this.args = args; - this.index = index; - this.filename = filename; -}; -tree.Call.prototype = { - // - // When evaluating a function call, - // we either find the function in `tree.functions` [1], - // in which case we call it, passing the evaluated arguments, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env) }), - result; - - if (this.name in tree.functions) { // 1. - try { - result = tree.functions[this.name].apply(tree.functions, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.filename }; - } - } - - // 2. - return new(tree.Anonymous)(this.name + - "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")"); - }, - - toCSS: function (env) { - return this.eval(env).toCSS(); - } -}; - -})(require('../tree')); -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; -tree.Color.prototype = { - eval: function () { return this }, - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - toCSS: function () { - if (this.alpha < 1.0) { - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(', ') + ")"; - } else { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - } - }, - - // - // 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. - // - operate: function (op, other) { - var result = []; - - if (! (other instanceof tree.Color)) { - other = other.toColor(); - } - - for (var c = 0; c < 3; c++) { - result[c] = tree.operate(op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(result, this.alpha + other.alpha); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { h: h * 360, s: s, l: l, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - - -})(require('../tree')); -(function (tree) { - -tree.Comment = function (value, silent) { - this.value = value; - this.silent = !!silent; -}; -tree.Comment.prototype = { - toCSS: function (env) { - return env.compress ? '' : this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype.eval = function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - var result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<'; - case 0: return op === '=' || op === '>=' || op === '=<'; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; -}; - -})(require('../tree')); -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - eval: function () { return this }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - toCSS: function () { - return this.unit.isEmpty() ? this.value : - (String(this.value) + this.unit.toCSS()); - }, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (op, other) { - var value = tree.operate(op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(other.unit.toCSS() !== unit.toCSS()) { - throw new Error("Incompatible units: '" + unit.toCSS() + - "' and '" + other.unit.toCSS() + "'."); - } - - value = tree.operate(op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, conversion, targetUnit; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(function (atomicUnit, denominator) { - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - } -}; - -tree.Unit = function (numerator, denominator) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; -}; - -tree.Unit.prototype = { - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0)); - }, - - toCSS: function () { - var i, css = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - css += "/" + this.denominator[i]; - } - return css; - }, - - compare: function (other) { - return this.is(other.toCSS()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toCSS() === unitString; - }, - - isEmpty: function () { - return this.numerator.length == 0 && this.denominator.length == 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, groupName, result = {}; - - for (groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(function (atomicUnit) { - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Directive = function (name, value) { - this.name = name; - - if (Array.isArray(value)) { - this.ruleset = new(tree.Ruleset)([], value); - this.ruleset.allowImports = true; - } else { - this.value = value; - } -}; -tree.Directive.prototype = { - toCSS: function (ctx, env) { - if (this.ruleset) { - this.ruleset.root = true; - return this.name + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - } else { - return this.name + ' ' + this.value.toCSS() + ';\n'; - } - }, - eval: function (env) { - var evaldDirective = this; - if (this.ruleset) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name); - evaldDirective.ruleset = this.ruleset.eval(env); - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) } -}; - -})(require('../tree')); -(function (tree) { - -tree.Element = function (combinator, value, index) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; -}; -tree.Element.prototype.eval = function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index); -}; -tree.Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value == '' && this.combinator.value.charAt(0) == '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype.toCSS = function (env) { - return { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : env.compress ? '+' : ' + ', - '~' : env.compress ? '~' : ' ~ ', - '>' : env.compress ? '>' : ' > ', - '|' : env.compress ? '|' : ' | ' - }[this.value]; -}; - -})(require('../tree')); -(function (tree) { - -tree.Expression = function (value) { this.value = value }; -tree.Expression.prototype = { - eval: function (env) { - if (this.value.length > 1) { - return new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return this; - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS ? e.toCSS(env) : ''; - }).join(' '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Extend = function Extend(elements, index) { - this.selector = new(tree.Selector)(elements); - this.index = index; -}; - -tree.Extend.prototype.eval = function Extend_eval(env, selectors) { - var selfSelectors = findSelfSelectors(selectors || env.selectors), - targetValue = this.selector.elements[0].value; - - env.frames.forEach(function(frame) { - frame.rulesets().forEach(function(rule) { - rule.selectors.forEach(function(selector) { - selector.elements.forEach(function(element, idx) { - if (element.value === targetValue) { - selfSelectors.forEach(function(_selector) { - _selector.elements[0] = new tree.Element( - element.combinator, - _selector.elements[0].value, - _selector.elements[0].index - ); - rule.selectors.push(new tree.Selector( - selector.elements - .slice(0, idx) - .concat(_selector.elements) - .concat(selector.elements.slice(idx + 1)) - )); - }); - } - }); - }); - }); - }); - return this; -}; - -function findSelfSelectors(selectors) { - var ret = []; - - (function loop(elem, i) { - if (selectors[i] && selectors[i].length) { - selectors[i].forEach(function(s) { - loop(s.elements.concat(elem), i + 1); - }); - } - else { - ret.push({ elements: elem }); - } - })([], 0); - - return ret; -} - - -})(require('../tree')); -(function (tree) { -// -// 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. -// -tree.Import = function (path, imports, features, once, index, rootpath) { - var that = this; - - this.once = once; - this.index = index; - this._path = path; - this.features = features && new(tree.Value)(features); - this.rootpath = rootpath; - - // The '.less' extension is optional - if (path instanceof tree.Quoted) { - this.path = /(\.[a-z]*$)|([\?;].*)$/.test(path.value) ? path.value : path.value + '.less'; - } else { - this.path = path.value.value || path.value; - } - - this.css = /css([\?;].*)?$/.test(this.path); - - // Only pre-compile .less files - if (! this.css) { - imports.push(this.path, function (e, root, imported) { - if (e) { e.index = index } - if (imported && that.once) that.skip = imported; - that.root = root || new(tree.Ruleset)([], []); - }); - } -}; - -// -// 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. -// -tree.Import.prototype = { - toCSS: function (env) { - var features = this.features ? ' ' + this.features.toCSS(env) : ''; - - if (this.css) { - // Add the base path if the import is relative - if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) { - this._path.value = this.rootpath + this._path.value; - } - return "@import " + this._path.toCSS() + features + ';\n'; - } else { - return ""; - } - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) return []; - - if (this.css) { - return this; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Keyword = function (value) { this.value = value }; -tree.Keyword.prototype = { - eval: function () { return this }, - toCSS: function () { return this.value }, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); -(function (tree) { - -tree.Media = function (value, features) { - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.ruleset = new(tree.Ruleset)(selectors, value); - this.ruleset.allowImports = true; -}; -tree.Media.prototype = { - toCSS: function (ctx, env) { - var features = this.features.toCSS(env); - - this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia); - return '@media ' + features + (env.compress ? '{' : ' {\n ') + - this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') + - (env.compress ? '}': '\n}\n'); - }, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], []); - if(this.debugInfo) { - this.ruleset.debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - media.features = this.features.eval(env); - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.ruleset); - media.ruleset = this.ruleset.eval(env); - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env) - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) }, - find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', 0); - return [new(tree.Selector)([el])]; - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, filename, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.filename = filename; - this.important = important; -}; -tree.mixin.Call.prototype = { - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1 } - else { return count } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - toCSS: function () { return "" }, - variable: function (name) { return this.parent.variable.call(this, name) }, - variables: function () { return this.parent.variables.call(this) }, - find: function () { return this.parent.find.apply(this, arguments) }, - rulesets: function () { return this.parent.rulesets.apply(this) }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex; - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) continue; - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments), - context, rules, start, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = important ? - this.parent.makeImportant.apply(this).rules : this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules).eval({ - frames: [this, frame].concat(mixinFrames) - }); - ruleset.originalRuleset = this; - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval({ - frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames) - })) { return false } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len, frame; - - if (! this.variadic) { - if (argsLength < this.required) { return false } - if (argsLength > this.params.length) { return false } - if ((this.required > 0) && (argsLength > this.params.length)) { return false } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Operation = function (op, operands) { - this.op = op.trim(); - this.operands = operands; -}; -tree.Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env), - temp; - - if (a instanceof tree.Dimension && b instanceof tree.Color) { - if (this.op === '*' || this.op === '+') { - temp = b, b = a, a = temp; - } else { - throw { name: "OperationError", - message: "Can't substract or divide a color from a number" }; - } - } - if (!a.operate) { - throw { name: "OperationError", - message: "Operation on an invalid type" }; - } - - return a.operate(this.op, b); -}; - -tree.operate = function (op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - toCSS: function (env) { - return '(' + this.value.toCSS(env).trim() + ')'; - }, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Quoted = function (str, content, escaped, i) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = i; -}; -tree.Quoted.prototype = { - toCSS: function () { - if (this.escaped) { - return this.value; - } else { - return this.quote + this.value + this.quote; - } - }, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index).eval(env); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ratio = function (value) { - this.value = value; -}; -tree.Ratio.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Rule = function (name, value, important, index, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.index = index; - this.inline = inline || false; - - if (name.charAt(0) === '@') { - this.variable = true; - } else { this.variable = false } -}; -tree.Rule.prototype.toCSS = function (env) { - if (this.variable) { return "" } - else { - return this.name + (env.compress ? ':' : ': ') + - this.value.toCSS(env) + - this.important + (this.inline ? "" : ";"); - } -}; - -tree.Rule.prototype.eval = function (context) { - return new(tree.Rule)(this.name, - this.value.eval(context), - this.important, - this.index, this.inline); -}; - -tree.Rule.prototype.makeImportant = function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.index, this.inline); -}; - -tree.Shorthand = function (a, b) { - this.a = a; - this.b = b; -}; - -tree.Shorthand.prototype = { - toCSS: function (env) { - return this.a.toCSS(env) + "/" + this.b.toCSS(env); - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (var i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - rules = ruleset.rules[i].eval(env); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - if (this.selectors) { - for (var i = 0; i < this.selectors.length; i++) { - if (this.selectors[i].extend) { - this.selectors[i].extend.eval(env, [[this.selectors[i]]].concat(env.selectors.slice(1))); - } - } - } - - // Evaluate everything else - for (var i = 0, rule; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], rule, match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key] } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > rule.selectors[j].elements.length) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(1)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - // - // Entry point for code generation - // - // `context` holds an array of arrays. - // - toCSS: function (context, env) { - var css = [], // The CSS output - rules = [], // node.Rule instances - _rules = [], // - rulesets = [], // node.Ruleset instances - paths = [], // Current selectors - selector, // The fully rendered selector - debugInfo, // Line number debugging - rule; - - if (! this.root) { - this.joinSelectors(paths, context, this.selectors); - } - - // Compile rules and rulesets - for (var i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - - if (rule.rules || (rule instanceof tree.Media)) { - rulesets.push(rule.toCSS(paths, env)); - } else if (rule instanceof tree.Directive) { - var cssValue = rule.toCSS(paths, env); - // Output only the first @charset definition as such - convert the others - // to comments in case debug is enabled - if (rule.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 (env.charset) { - if (rule.debugInfo) { - rulesets.push(tree.debugInfo(env, rule)); - rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env)); - } - continue; - } - env.charset = true; - } - rulesets.push(cssValue); - } else if (rule instanceof tree.Comment) { - if (!rule.silent) { - if (this.root) { - rulesets.push(rule.toCSS(env)); - } else { - rules.push(rule.toCSS(env)); - } - } - } else { - if (rule.toCSS && !rule.variable) { - rules.push(rule.toCSS(env)); - } else if (rule.value && !rule.variable) { - rules.push(rule.value.toString()); - } - } - } - - rulesets = rulesets.join(''); - - // If this is the root node, we don't render - // a selector, or {}. - // Otherwise, only output if this ruleset has rules. - if (this.root) { - css.push(rules.join(env.compress ? '' : '\n')); - } else { - if (rules.length > 0) { - debugInfo = tree.debugInfo(env, this); - selector = paths.map(function (p) { - return p.map(function (s) { - return s.toCSS(env); - }).join('').trim(); - }).join(env.compress ? ',' : ',\n'); - - // Remove duplicates - for (var i = rules.length - 1; i >= 0; i--) { - if (_rules.indexOf(rules[i]) === -1) { - _rules.unshift(rules[i]); - } - } - rules = _rules; - - css.push(debugInfo + selector + - (env.compress ? '{' : ' {\n ') + - rules.join(env.compress ? '' : '\n ') + - (env.compress ? '}' : '\n}\n')); - } - } - css.push(rulesets); - - return css.join('') + (env.compress ? '\n' : ''); - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for(i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for(j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length == 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, "")); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for(k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = new(tree.Selector)([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for(i = 0; i < newSelectors.length; i++) { - paths.push(newSelectors[i]); - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length == 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for(i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); -(function (tree) { - -tree.Selector = function (elements, extend) { - this.elements = elements; - this.extend = extend; -}; -tree.Selector.prototype.match = function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen) - - if (olen === 0 || len < olen) { - return false; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return false; - } - } - } - return true; -}; -tree.Selector.prototype.eval = function (env) { - return new(tree.Selector)(this.elements.map(function (e) { - return e.eval(env); - }), this.extend); -}; -tree.Selector.prototype.toCSS = function (env) { - if (this._css) { return this._css } - - if (this.elements[0].combinator.value === "") { - this._css = ' '; - } else { - this._css = ''; - } - - this._css += this.elements.map(function (e) { - if (typeof(e) === 'string') { - return ' ' + e.trim(); - } else { - return e.toCSS(env); - } - }).join(''); - - return this._css; -}; - -})(require('../tree')); -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - toCSS: function (env) { - return this.value; - }, - eval: function () { return this } -}; - -})(require('../tree')); -(function (tree) { - -tree.URL = function (val, rootpath) { - this.value = val; - this.rootpath = rootpath; -}; -tree.URL.prototype = { - toCSS: function () { - return "url(" + this.value.toCSS() + ")"; - }, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) { - rootpath = this.rootpath; - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - return new(tree.URL)(val, this.rootpath); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Value = function (value) { - this.value = value; - this.is = 'value'; -}; -tree.Value.prototype = { - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - toCSS: function (env) { - return this.value.map(function (e) { - return e.toCSS(env); - }).join(env.compress ? ',' : ', '); - } -}; - -})(require('../tree')); -(function (tree) { - -tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file }; -tree.Variable.prototype = { - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') == 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.file, - index: this.index }; - } - } -}; - -})(require('../tree')); -(function (tree) { - -tree.debugInfo = function(env, ctx) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r } - } - return null; -}; -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -})(require('./tree')); -var name; - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return error(e, sheetName); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - error(e, sheetName); - } - }); -} - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - var output, - compress = false, - i; - - for(i = 0; i < args.length; i++) { - switch(args[i]) { - case "-x": - compress = true; - break; - default: - if (!name) { - name = args[i]; - } else if (!output) { - output = args[i]; - } else { - print("unrecognised parameters"); - print("input_file [output_file] [-x]"); - } - } - } - - if (!name) { - print('No files present in the fileset; Check your pattern match in build.xml'); - quit(1); - } - path = name.split("/");path.pop();path=path.join("/") - - var input = readFile(name); - - if (!input) { - print('lesscss: couldn\'t open file ' + name); - quit(1); - } - - var result; - try { - var parser = new less.Parser(); - parser.parse(input, function (e, root) { - if (e) { - error(e, name); - quit(1); - } else { - result = root.toCSS({compress: compress || false}); - if (output) { - writeFile(output, result); - print("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - error(e, name); - quit(1); - } - print("done"); -}(arguments)); - -function error(e, filename) { - - var content = "Error : " + filename + "\n"; - - filename = e.filename || filename; - - if (e.message) { - content += e.message + "\n"; - } - - var errorline = function (e, i, classname) { - if (e.extract[i]) { - content += - String(parseInt(e.line) + (i - 1)) + - ":" + e.extract[i] + "\n"; - } - }; - - if (e.stack) { - content += e.stack; - } else if (e.extract) { - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n'; - errorline(e, 0); - errorline(e, 1); - errorline(e, 2); - } - print(content); -} \ No newline at end of file From 3322609de5fe0ff6fc3d125ed6df7393a9ab0484 Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Thu, 23 Jan 2014 16:08:10 +0400 Subject: [PATCH 003/161] More consistent named colour variables. --- lib/less/functions.js | 28 ++++++------- lib/less/parser.js | 12 ++---- lib/less/tree/color.js | 55 +++++++++++++------------ test/browser/css/global-vars/simple.css | 2 +- test/browser/css/modify-vars/simple.css | 4 +- test/css/comments.css | 4 +- test/css/css-3.css | 2 +- test/css/css-escapes.css | 2 +- test/css/css.css | 2 +- test/css/extract-and-length.css | 2 +- test/css/functions.css | 5 ++- test/css/globalVars/simple.css | 2 +- test/css/import-once.css | 2 +- test/css/import.css | 6 +-- test/css/mixins-args.css | 4 +- test/css/mixins-guards-default-func.css | 4 +- test/css/mixins-guards.css | 6 +-- test/css/parens.css | 2 +- test/css/scope.css | 14 +++---- test/css/selectors.css | 8 ++-- test/css/strings.css | 8 ++-- test/css/whitespace.css | 4 +- test/less/functions.less | 5 ++- test/sourcemaps/basic.json | 2 +- 24 files changed, 93 insertions(+), 92 deletions(-) diff --git a/lib/less/functions.js b/lib/less/functions.js index 8c0f1031c..ffef4ec44 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -310,21 +310,19 @@ tree.functions = { percentage: function (n) { return new(tree.Dimension)(n.value * 100, '%'); }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } + color: function(c) { + if ((c instanceof tree.Quoted) && + (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { + return new(tree.Color)(c.value.slice(1)); + } + if ((c instanceof tree.Color) || (c = tree.Color.fromKeyword(c.value))) { + c.keyword = undefined; + return c; + } + throw { + type: "Argument", + message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" + }; }, iscolor: function (n) { return this._isa(n, tree.Color); diff --git a/lib/less/parser.js b/lib/less/parser.js index 70d2256ba..20289e101 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -793,15 +793,9 @@ less.Parser = function Parser(env) { // black border-collapse // keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); + var k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/); if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); + return tree.Color.fromKeyword(k) || new(tree.Keyword)(k); } }, @@ -1960,4 +1954,4 @@ less.Parser.serializeVars = function(vars) { } return s; -}; \ No newline at end of file +}; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 8ea3fe020..3d841b6b6 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -23,8 +23,6 @@ tree.Color = function (rgb, a) { this.alpha = typeof(a) === 'number' ? a : 1; }; -var transparentKeyword = "transparent"; - tree.Color.prototype = { type: "Color", eval: function () { return this; }, @@ -34,35 +32,39 @@ tree.Color.prototype = { output.add(this.toCSS(env)); }, toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, this.alpha); + var compress = env && env.compress && !doNotCompress, color, alpha; + + // `keyword` is set if this color was originally + // converted from a named color string so we need + // to respect this and try to output named color too. + if (this.keyword) { + return this.keyword; + } // 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. + alpha = tree.fround(env, this.alpha); if (alpha < 1) { - if (alpha === 0 && this.isTransparentKeyword) { - return transparentKeyword; - } return "rgba(" + this.rgb.map(function (c) { return clamp(Math.round(c), 255); }).concat(clamp(alpha, 1)) .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); + } + + color = this.toRGB(); - if (compress) { - var splitcolor = color.split(''); + if (compress) { + var splitcolor = color.split(''); - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; - } + // Convert color to short format + if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { + color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; } - - return color; } + + return color; }, // @@ -152,16 +154,17 @@ tree.Color.prototype = { }; tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); + var c, key = keyword.toLowerCase(); + if (tree.colors.hasOwnProperty(key)) { + c = new(tree.Color)(tree.colors[key].slice(1)); + } + else if (key === "transparent") { + c = new(tree.Color)([0, 0, 0], 0); } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; + + if (c) { + c.keyword = keyword; + return c; } }; diff --git a/test/browser/css/global-vars/simple.css b/test/browser/css/global-vars/simple.css index 05b9fb02f..6446ebfd4 100644 --- a/test/browser/css/global-vars/simple.css +++ b/test/browser/css/global-vars/simple.css @@ -1,3 +1,3 @@ .test { - color: #ff0000; + color: red; } diff --git a/test/browser/css/modify-vars/simple.css b/test/browser/css/modify-vars/simple.css index 889cd53d9..2a58574d8 100644 --- a/test/browser/css/modify-vars/simple.css +++ b/test/browser/css/modify-vars/simple.css @@ -2,7 +2,7 @@ color: gainsboro; } .test { - color1: #008000; - color2: #800080; + color1: green; + color2: purple; scalar: 20; } diff --git a/test/css/comments.css b/test/css/comments.css index b85f5b4f1..3d86fb77a 100644 --- a/test/css/comments.css +++ b/test/css/comments.css @@ -48,7 +48,7 @@ .selector, .lots, .comments { - color: #808080, /* blue */ #ffa500; + color: grey, /* blue */ orange; -webkit-border-radius: 2px /* webkit only */; -moz-border-radius: 8px /* moz only with operation */; } @@ -56,7 +56,7 @@ color: 1px; } #last { - color: #0000ff; + color: blue; } /* */ /* { */ diff --git a/test/css/css-3.css b/test/css/css-3.css index 61f635c27..7d4f16cf1 100644 --- a/test/css/css-3.css +++ b/test/css/css-3.css @@ -1,5 +1,5 @@ .comma-delimited { - text-shadow: -1px -1px 1px #ff0000, 6px 5px 5px #ffff00; + text-shadow: -1px -1px 1px red, 6px 5px 5px yellow; -moz-box-shadow: 0pt 0pt 2px rgba(255, 255, 255, 0.4) inset, 0pt 4px 6px rgba(255, 255, 255, 0.4) inset; -webkit-transform: rotate(0deg); } diff --git a/test/css/css-escapes.css b/test/css/css-escapes.css index 4d343aa62..4ebecfe15 100644 --- a/test/css/css-escapes.css +++ b/test/css/css-escapes.css @@ -8,7 +8,7 @@ background: red; } .\34 04 strong { - color: #ff00ff; + color: fuchsia; font-weight: bold; } .trailingTest\+ { diff --git a/test/css/css.css b/test/css/css.css index b011a7e38..4fb579711 100644 --- a/test/css/css.css +++ b/test/css/css.css @@ -71,7 +71,7 @@ p + h1 { display: -moz-inline-stack; width: .1em; background-color: #009998; - background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), to(#0000ff)); + background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); margin: ; filter: alpha(opacity=100); width: auto\9; diff --git a/test/css/extract-and-length.css b/test/css/extract-and-length.css index f550e201b..8bb5ae984 100644 --- a/test/css/extract-and-length.css +++ b/test/css/extract-and-length.css @@ -13,7 +13,7 @@ name-value: name; string-value: "string"; number-value: 12345678; - color-value: #0000ff; + color-value: blue; rgba-value: rgba(80, 160, 240, 0.67); empty-value: ; name-length: 1; diff --git a/test/css/functions.css b/test/css/functions.css index 2c7896df1..5401cc50f 100644 --- a/test/css/functions.css +++ b/test/css/functions.css @@ -87,7 +87,10 @@ max: 3; max: max(8%, 1cm); percentage: 20%; - color: #ff0011; + color-quoted-digit: #dda0dd; + color-quoted-keyword: #dda0dd; + color-color: #dda0dd; + color-keyword: #dda0dd; tint: #898989; tint-full: #ffffff; tint-percent: #898989; diff --git a/test/css/globalVars/simple.css b/test/css/globalVars/simple.css index 55779d8b2..630cc4c13 100644 --- a/test/css/globalVars/simple.css +++ b/test/css/globalVars/simple.css @@ -2,5 +2,5 @@ * Test */ .class { - color: #ff0000; + color: red; } diff --git a/test/css/import-once.css b/test/css/import-once.css index 2f86b3b34..f90531ec0 100644 --- a/test/css/import-once.css +++ b/test/css/import-once.css @@ -1,5 +1,5 @@ #import { - color: #ff0000; + color: red; } body { width: 100%; diff --git a/test/css/import.css b/test/css/import.css index a37491813..51e753f80 100644 --- a/test/css/import.css +++ b/test/css/import.css @@ -3,7 +3,7 @@ @import url("//ha.com/file.css") (min-width: 100px); #import-test { height: 10px; - color: #ff0000; + color: red; width: 10px; height: 30%; } @@ -13,11 +13,11 @@ } } #import { - color: #ff0000; + color: red; } .mixin { height: 10px; - color: #ff0000; + color: red; } @media screen and (max-width: 601px) { #css { diff --git a/test/css/mixins-args.css b/test/css/mixins-args.css index 2b6c5c962..6e9c914e5 100644 --- a/test/css/mixins-args.css +++ b/test/css/mixins-args.css @@ -8,7 +8,7 @@ color: blue; width: 10px; height: 99%; - border: 2px dotted #000000; + border: 2px dotted black; } .one-arg { width: 15px; @@ -52,7 +52,7 @@ body { width: 10px; } .arguments { - border: 1px solid #000000; + border: 1px solid black; width: 1px; } .arguments2 { diff --git a/test/css/mixins-guards-default-func.css b/test/css/mixins-guards-default-func.css index 4b6910c34..a456fb2e4 100644 --- a/test/css/mixins-guards-default-func.css +++ b/test/css/mixins-guards-default-func.css @@ -96,10 +96,10 @@ guard-default-multi-2-3 { default-3: 3; } guard-default-multi-3-blue { - case-2: #00008b; + case-2: darkblue; } guard-default-multi-3-green { - default-color: #008000; + default-color: green; } guard-default-multi-3-foo { case-1: I am 'foo'; diff --git a/test/css/mixins-guards.css b/test/css/mixins-guards.css index 25e6f2873..516d25433 100644 --- a/test/css/mixins-guards.css +++ b/test/css/mixins-guards.css @@ -62,9 +62,9 @@ test: pass; } .colorguardtest { - content: is #ff0000; - content: is not #0000ff its #ff0000; - content: is not #0000ff its #800080; + content: is red; + content: is not blue its red; + content: is not blue its purple; } .stringguardtest { content: is theme1; diff --git a/test/css/parens.css b/test/css/parens.css index dc09fdf52..0e8cc7c8f 100644 --- a/test/css/parens.css +++ b/test/css/parens.css @@ -1,5 +1,5 @@ .parens { - border: 2px solid #000000; + border: 2px solid black; margin: 1px 3px 16 3; width: 36; padding: 2px 36px; diff --git a/test/css/scope.css b/test/css/scope.css index baa055230..3ffcca0d4 100644 --- a/test/css/scope.css +++ b/test/css/scope.css @@ -2,19 +2,19 @@ color: #998899; } .scope1 { - color: #0000ff; - border-color: #000000; + color: blue; + border-color: black; } .scope1 .scope2 { - color: #0000ff; + color: blue; } .scope1 .scope2 .scope3 { - color: #ff0000; - border-color: #000000; - background-color: #ffffff; + color: red; + border-color: black; + background-color: white; } .scope { - scoped-val: #008000; + scoped-val: green; } .heightIsSet { height: 1024px; diff --git a/test/css/selectors.css b/test/css/selectors.css index 510550472..7dd6139dc 100644 --- a/test/css/selectors.css +++ b/test/css/selectors.css @@ -102,16 +102,16 @@ p a span { background: amber; } .other ::fnord { - color: #ff0000; + color: red; } .other::fnord { - color: #ff0000; + color: red; } .other ::bnord { - color: #ff0000; + color: red; } .other::bnord { - color: #ff0000; + color: red; } .blood { color: red; diff --git a/test/css/strings.css b/test/css/strings.css index cd6d60202..54298b138 100644 --- a/test/css/strings.css +++ b/test/css/strings.css @@ -33,10 +33,10 @@ url5: "http://lesscss.org/54.4px"; } .mix-mul-class { - color: #0000ff; - color: #ff0000; - color: #000000; - color: #ffa500; + color: blue; + color: red; + color: black; + color: orange; } .watermark { family: Univers, Arial, Verdana, San-Serif; diff --git a/test/css/whitespace.css b/test/css/whitespace.css index 74c9b65e6..38ad81c1c 100644 --- a/test/css/whitespace.css +++ b/test/css/whitespace.css @@ -19,14 +19,14 @@ color: white; } .no-semi-column { - color: #ffffff; + color: white; } .no-semi-column { color: white; white-space: pre; } .no-semi-column { - border: 2px solid #ffffff; + border: 2px solid white; } .newlines { background: the, diff --git a/test/less/functions.less b/test/less/functions.less index 170a915b4..9772c93cc 100644 --- a/test/less/functions.less +++ b/test/less/functions.less @@ -93,7 +93,10 @@ max: max(1, 3); max: max(3%, 1cm, 8%, 2mm); percentage: percentage((10px / 50)); - color: color("#ff0011"); + color-quoted-digit: color("#dda0dd"); + color-quoted-keyword: color("plum"); + color-color: color(#dda0dd); + color-keyword: color(plum); tint: tint(#777777, 13); tint-full: tint(#777777, 100); tint-percent: tint(#777777, 13%); diff --git a/test/sourcemaps/basic.json b/test/sourcemaps/basic.json index ab73305da..5bc5b2e30 100644 --- a/test/sourcemaps/basic.json +++ b/test/sourcemaps/basic.json @@ -1 +1 @@ -{"version":3,"file":"sourcemaps/basic.css","sources":["testweb/sourcemaps/imported.css","testweb/sourcemaps/basic.less"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;ACAA;EACE,YAAA;EAJA,UAAA;EAWA,iBAAA;EALA,WAAA;EACA,mBAAA;;AAJF,EASE;AATF,EASM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;EAGA,UAAA;;AALN;AAAI;AAUJ;EATE,iBAAA;;AADF,EAEE;AAFE,EAEF;AAFF,EAEM;AAFF,EAEE;AAQN,OARE;AAQF,OARM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAQN,OARE,GAQF,UARE;AAQF,OARE,GAEI,KAFJ;AAQF,OARE,GAQF,UARM;AAQN,OARE,GAEI,KAFA;AAEF,EAFF,GAQF,UARE;AAEE,EAFF,GAQF,UARM;AAQN,OARM,GAQN,UARE;AAQF,OARM,GAEA,KAFJ;AAQF,OARM,GAQN,UARM;AAQN,OARM,GAEA,KAFA;AAEF,EAFE,GAQN,UARE;AAEE,EAFE,GAQN,UARM;EAGA,UAAA;;AAKN;EACE,WAAA"} \ No newline at end of file +{"version":3,"file":"sourcemaps/basic.css","sources":["testweb/sourcemaps/imported.css","testweb/sourcemaps/basic.less"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;ACAA;EACE,YAAA;EAJA,UAAA;EAWA,iBAAA;EALA,WAAA;EACA,iBAAA;;AAJF,EASE;AATF,EASM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;EAGA,UAAA;;AALN;AAAI;AAUJ;EATE,iBAAA;;AADF,EAEE;AAFE,EAEF;AAFF,EAEM;AAFF,EAEE;AAQN,OARE;AAQF,OARM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAQN,OARE,GAQF,UARE;AAQF,OARE,GAEI,KAFJ;AAQF,OARE,GAQF,UARM;AAQN,OARE,GAEI,KAFA;AAEF,EAFF,GAQF,UARE;AAEE,EAFF,GAQF,UARM;AAQN,OARM,GAQN,UARE;AAQF,OARM,GAEA,KAFJ;AAQF,OARM,GAQN,UARM;AAQN,OARM,GAEA,KAFA;AAEF,EAFE,GAQN,UARE;AAEE,EAFE,GAQN,UARM;EAGA,UAAA;;AAKN;EACE,WAAA"} \ No newline at end of file From e1877d8a2636467432a81c2757f2032b8fdc773b Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Thu, 23 Jan 2014 16:12:45 +0400 Subject: [PATCH 004/161] More consistent named colour variables. Updated. --- lib/less/parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less/parser.js b/lib/less/parser.js index 20289e101..c6031a9cc 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -793,7 +793,7 @@ less.Parser = function Parser(env) { // black border-collapse // keyword: function () { - var k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/); + var k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); if (k) { return tree.Color.fromKeyword(k) || new(tree.Keyword)(k); } From 7b0ce1e4c71ee148022cc82a9f23b77c611bafc5 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 13 Feb 2014 21:57:49 +0000 Subject: [PATCH 005/161] ignore directories not needed for node --- .npmignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.npmignore b/.npmignore index bc8ad3f73..9e9b12e44 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,5 @@ .gitattributes +build/ +benchmark/ +test/ +gradle/ \ No newline at end of file From 21165a34ebb3e890c328ca0d12bf1ab47cd9defb Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 13 Feb 2014 22:40:21 +0000 Subject: [PATCH 006/161] remove other dist files and update gruntfile not to generate rhino lessc --- Gruntfile.js | 2 +- dist/less-1.6.0.js | 7485 ------------------------------- dist/less-1.6.0.min.js | 16 - dist/less-1.6.1.js | 7513 ------------------------------- dist/less-1.6.1.min.js | 16 - dist/less-1.6.2.js | 7624 -------------------------------- dist/less-1.6.2.min.js | 16 - dist/less-1.6.3.js | 7627 -------------------------------- dist/less-1.6.3.min.js | 16 - dist/less-rhino-1.5.1.js | 6831 ----------------------------- dist/less-rhino-1.6.2.js | 9017 ------------------------------------- dist/less-rhino-1.6.3.js | 9020 -------------------------------------- 12 files changed, 1 insertion(+), 55182 deletions(-) delete mode 100644 dist/less-1.6.0.js delete mode 100644 dist/less-1.6.0.min.js delete mode 100644 dist/less-1.6.1.js delete mode 100644 dist/less-1.6.1.min.js delete mode 100644 dist/less-1.6.2.js delete mode 100644 dist/less-1.6.2.min.js delete mode 100644 dist/less-1.6.3.js delete mode 100644 dist/less-1.6.3.min.js delete mode 100644 dist/less-rhino-1.5.1.js delete mode 100644 dist/less-rhino-1.6.2.js delete mode 100644 dist/less-rhino-1.6.3.js diff --git a/Gruntfile.js b/Gruntfile.js index b70ed36a1..63dbd5152 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -78,7 +78,7 @@ module.exports = function(grunt) { footer: '' // override task-level footer }, src: ['<%= build.rhinolessc %>'], - dest: 'dist/lessc-rhino-<%= pkg.version %>.js' + dest: 'dist/lessc-rhino.js' }, // Generate readme readme: { diff --git a/dist/less-1.6.0.js b/dist/less-1.6.0.js deleted file mode 100644 index 07dfa78b7..000000000 --- a/dist/less-1.6.0.js +++ /dev/null @@ -1,7485 +0,0 @@ -/*! - * LESS - Leaner CSS v1.6.0 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = current; memo = currentPos = i; } - function restore() { current = temp; currentPos = i = memo; } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = (typeof arg === "function") ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastClosing, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; continue; - case 41: // ) - parenLevel--; continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; lastOpening = parserCurrentIndex; continue; - case 125: // } - level--; lastClosing = parserCurrentIndex; - if (!level) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if (level > 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } - return fail("missing opening `{`", lastClosing); - } else if (parenLevel !== 0) { - return fail((parenLevel > 0) ? "missing closing `)`" : "missing opening `(`"); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (true) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, i, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (elements && ($char(';') || peekChar('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - while (true) { - if (isCall) { - arg = parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(parsers.expression); - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$char(')')) { - furthest = i; - restore(); - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - name = this.variable() || this.ruleProperty(); - if (name) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // a name returned by this.ruleProperty() is always an array of the form: - // ["", "string-1", ..., "string-n", ""] or ["", "string-1", ..., "string-n", "+"] - merge = name.pop && (name.pop() === "+"); - - if (value && this.end()) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($re(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) { - rules = this.block(); - if (rules) { - return new(tree.Directive)(name, rules, i, env.currentFileInfo); - } - } else { - value = hasExpression ? this.expression() : this.entity(); - if (value && $char(';')) { - var directive = new(tree.Directive)(name, value, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert @{var}s to tree.Variable(s) and return: - skipWhitespace(length); - for (var k in name) { - if (name[k].charAt(0) === '@') { - name[k] = new tree.Variable('@' + name[k].slice(2, -1), - index[k], env.currentFileInfo); - } - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - // only works in node, needs interface to what is supported in environment - try { - returner = new Buffer(returner).toString('base64'); - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - - // color blending - for (f in colorBlendMode) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - - // default - f = tree.defaultFunc; - tf.default = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - - // 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 (this.alpha < 1) { - if (this.alpha === 0 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(this.alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)(null, value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - if (this.value) { - this.value = visitor.visit(this.value); - } - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); - return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule; - var candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, defaultUsed = false; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - defaultFunc.value(f); - conditionResult[f] = mixin.matchCondition(args, env); - } - if (conditionResult[0] || conditionResult[1]) { - if (conditionResult[0] != conditionResult[1]) { - if (defaultUsed) { - // todo: ideally, it would make sense to also print the candidate - // mixin definitions that cause the conflict (current one and the - // mixin that set defaultUsed flag). But is there any easy method - // to get their filename/line/index info here? - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - - defaultUsed = true; - candidate.matchIfDefault = true; - candidate.matchIfDefaultValue = conditionResult[1]; - } - - candidates.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - for (m in candidates) { - candidate = candidates[m]; - if (!candidate.matchIfDefault || (candidate.matchIfDefaultValue == (candidates.length == 1))) { - try { - mixin = candidate.mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - //if (this.important) { - // isImportant = env.isImportant; - // env.isImportant = true; - //} - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - //if (this.important) { - // env.isImportant = isImportant; - //} - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false; - var name = this.name.map ? - this.name.map( function(v) { - return v.eval ? v.eval(env).value : v; - }).join('') : this.name; - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (e.index === undefined) { - e.index = this.index; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, i, defaultFunc = tree.defaultFunc; - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selectors.push(thisSelectors[i].eval(env)); - } - defaultFunc.reset(); - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition) { - rsRules[i].frames = envFrames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRuleCnt; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition)) { - rsRules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, i; - - oelements = other.elements.map( function(v) { - return v.combinator.value + (v.value.value || v.value); - }).join("").match(/[,&#\.\w-]([\w-]|(\\.))*/g); - // ^ regexp could be more simple but see test/less/css-escapes.less:17, doh! - - if (!oelements) { - return 0; - } - - if (oelements[0] === "&") { - oelements.shift(); - } - - olen = oelements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== oelements[i]) { - return 0; - } - } - } - return olen; // return number of matched elements - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple'// whether we are currently importing multiple copies - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info; - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - less.tree.functions[func] = less.functions[func]; - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.info); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.6.0.min.js b/dist/less-1.6.0.min.js deleted file mode 100644 index 3f57252ac..000000000 --- a/dist/less-1.6.0.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * LESS - Leaner CSS v1.6.0 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"development"==v.env&&"undefined"!=typeof console&&v.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,y.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&C){d("saving "+f+" to cache.",y.info);try{C.setItem(f,a),C.setItem(f+":timestamp",c)}catch(k){d("failed to save",y.errors)}}}function h(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==v.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function i(a,b){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?f(a,b):"function"==typeof v.errorReporting&&v.errorReporting("add",a,b):h(a,b)}function j(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function k(){}function l(a){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?k(a):"function"==typeof v.errorReporting&&v.errorReporting("remove",a):j(a)}function m(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function o(a,b){var c,d,e,f,g=n(a),h=n(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=p(),h=x?v.fileAsync:v.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",y.info),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),x&&!v.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function r(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=n(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=v.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?n(e.rootpath+o(f.path,h.entryPath)).path:f.path),e.useFileCache&&D[g])try{var i=D[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else q(g,e.mime,function(a,b){D[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function s(a,b,c,d,e){var f=new v.tree.parseEnv(v);f.mime=a.type,(e||v.globalVars)&&(f.useFileCache=!0),r(a.href,null,function(h,i,j,k,m){if(m){m.remaining=d;var n=C&&C.getItem(j),o=C&&C.getItem(j+":timestamp");if(!c&&o&&m.lastModified&&new Date(m.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),m.local=!0,b(null,null,i,a,m,j),void 0}l(j),i?(f.currentFileInfo=k,new v.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,m,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:v.globalVars})):b(h,null,null,a,m,j)},f,e)}function t(a,b,c){for(var d=0;dE&&(D=D.slice(x-E),E=x)}function g(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function h(a){var b,c,d=typeof a;return"string"===d?u.charAt(x)!==a?null:(k(1),a):(f(),(b=a.exec(D))?(c=b[0].length,k(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function i(a){x>E&&(D=D.slice(x-E),E=x);var b=a.exec(D);return b?(k(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function j(a){return u.charAt(x)!==a?null:(k(1),a)}function k(a){for(var b,c=x,d=y,e=x-E,f=x+D.length-e,g=x+=a,h=u;f>x&&(b=h.charCodeAt(x),!(b>32))&&(32===b||10===b||9===b||13===b);x++);return D=D.slice(a+x-g+e),E=x,!D.length&&y=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function s(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==v.mode&&"rhino"!==v.mode&&(e=c("path").resolve(e)),{lineNumber:r(a,b).line+1,fileName:e}}function t(a,b){var c=q(a,b),d=r(a.index,c),e=d.line,f=d.column,g=a.call&&r(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var u,x,y,z,A,B,C,D,E,F,G,H=a&&a.filename;a instanceof w.parseEnv||(a=new w.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files||d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};v.Parser.importer?v.Parser.importer(b,c,g,a):v.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new w.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new v.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=i;return t.prototype=new Error,t.prototype.constructor=t,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,F={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(x=y=E=B=0,j=f&&f.globalVars?v.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+v.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,F.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),u=d=m+d.replace(/^\uFEFF/,"")+k,F.imports.contents[a.currentFileInfo.filename]=d,C=function(b){function c(b,c){l=new t({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++;continue;case 41:q--;continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:p--,f=i,p||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j)}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?p>0?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):c("missing opening `{`",f):0!==q?c(q>0?"missing closing `)`":"missing opening `(`"):(d(!0),r)}(d),l)return e(new t(l,a));D=C[0];try{g=new w.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new t(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new w.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof w.Value||(b instanceof w.Expression||(b=new w.Expression([b])),b=new w.Value([b])),new w.Rule("@"+a,b,!1,null,0)}),i.frames=[new w.Ruleset(null,f)]);try{var j,k=[],l=[new w.joinSelectorVisitor,new w.processExtendsVisitor,new w.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=i(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new w.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=i(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new w.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=x;return"~"===u.charAt(e)&&(e++,d=!0),"`"===u.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||n("You are using JavaScript, which has been disabled."),d&&j("~"),c=i(/^`([^`]*)`/),c?new w.JavaScript(c[1],x,d):void 0):void 0}},variable:function(){var a;return"@"===u.charAt(x)&&(a=i(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e,f,g=x;if(a?i(/^&:extend\(/):i(/^:extend\(/)){do{for(d=null,b=null;!(d=i(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new w.Extend(new w.Selector(b),d,g),e?e.push(f):e=[f]}while(j(","));return l(/^\)/),a&&l(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,f,g,h,k=u.charAt(x),l=!1,n=x;if("."===k||"#"===k){for(d();;){if(f=i(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!f)break;c=new w.Element(g,f,x,a.currentFileInfo),b?b.push(c):b=[c],g=j(">")}return j("(")&&(h=this.args(!0).args,m(")")),G.important()&&(l=!0),b&&(j(";")||p("}"))?new w.mixin.Call(b,h,n,a.currentFileInfo,l):(e(),void 0)}},args:function(a){for(var b,c,d,e,f,g,h=F.parsers,k=h.entities,m={args:null,variadic:!1},o=[],p=[],q=[];;){if(a)g=h.expression();else{if(h.comments(),"."===u.charAt(x)&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({variadic:!0});break}g=k.variable()||k.literal()||k.keyword()}if(!g)break;e=null,g.throwAwayComments&&g.throwAwayComments(),f=g;var r=null;if(a?1==g.value.length&&(r=g.value[0]):r=g,r&&r instanceof w.Variable)if(j(":"))o.length>0&&(b&&n("Cannot mix ; and , as delimiter types"),c=!0),f=l(h.expression),e=d=r.name;else{if(!a&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({name:g.name,variadic:!0});break}a||(d=e=r.name,f=null)}f&&o.push(f),q.push({name:e,value:f}),j(",")||(j(";")||b)&&(c&&n("Cannot mix ; and , as delimiter types"),b=!0,o.length>1&&(f=new w.Value(o)),p.push({name:d,value:f}),d=null,o=[],c=!1)}return m.args=b?p:q,m},definition:function(){var a,b,c,f,g=[],h=!1;if(!("."!==u.charAt(x)&&"#"!==u.charAt(x)||o(/^[^{]*\}/))&&(d(),b=i(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=b[1];var k=this.args(!1);if(g=k.args,h=k.variadic,j(")")||(B=x,e()),G.comments(),i(/^when/)&&(f=l(G.conditions,"expected condition")),c=G.block())return new w.mixin.Definition(a,g,c,f,h);e()}}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return j(";")||p("}")},alpha:function(){var a;if(i(/^\(opacity=/i))return a=i(/^\d+/)||this.entities.variable(),a?(m(")"),new w.Alpha(a)):void 0},element:function(){var b,c,d;return c=this.combinator(),b=i(/^(?:\d+\.\d+|\d+)%/)||i(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||j("*")||j("&")||this.attribute()||i(/^\([^()@]+\)/)||i(/^[\.#](?=@)/)||this.entities.variableCurly(),b||j("(")&&(d=this.selector())&&j(")")&&(b=new w.Paren(d)),b?new w.Element(c,b,x,a.currentFileInfo):void 0},combinator:function(){var a=u.charAt(x);if(">"===a||"+"===a||"~"===a||"|"===a){for(x++;g(u,x);)x++;return new w.Combinator(a)}return g(u,x-1)?new w.Combinator(" "):new w.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=J;(b&&(g=this.extend())||b&&(h=j(/^when/))||(f=this.element()))&&(h?i=l(this.conditions,"expected condition"):i?n("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&n("Extend can only be used at the end of selector"),e=u.charAt(x),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new w.Selector(c,d,i,x,a.currentFileInfo):(d&&n("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){if(j("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=l(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=i(/^[|~*$^]?=/),c&&(b=d.quoted()||i(/^[0-9]+%/)||i(/^[\w-]+/)||d.variableCurly()),m("]"),new w.Attribute(a,c,b)}},block:function(){var a;return j("{")&&(a=this.primary())&&j("}")?a:void 0},ruleset:function(){var b,c,f,g;for(d(),a.dumpLineNumbers&&(g=s(x,u,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),!j(","))break;c.condition&&n("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(f=this.block())){var h=new w.Ruleset(b,f,a.strictImports);return a.dumpLineNumbers&&(h.debugInfo=g),h}B=x,e()},rule:function(b){var c,f,g,h=u.charAt(x),i=!1;if(d(),"."!==h&&"#"!==h&&"&"!==h&&(c=this.variable()||this.ruleProperty())){if(f=!b&&(a.compress||c.charAt&&"@"===c.charAt(0))?this.value()||this.anonymousValue():this.anonymousValue()||this.value(),g=this.important(),i=c.pop&&"+"===c.pop(),f&&this.end())return new w.Rule(c,f,g,i,A,a.currentFileInfo);if(B=x,e(),f&&!b)return this.rule(!0)}},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(D),a?(x+=a[0].length-1,new w.Anonymous(a[1])):void 0},"import":function(){var b,c,f=x;d();var g=i(/^@import?\s+/),h=(g?this.importOptions():null)||{};return g&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),j(";"))?(c=c&&new w.Value(c),new w.Import(b,c,h,f,a.currentFileInfo)):(e(),void 0)},importOptions:function(){var a,b,c,d={};if(!j("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!j(","))break}while(a);return m(")"),d},importOption:function(){var a=i(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(j("(")){if(c=this.property(),b=this.value(),!j(")"))return null;if(c&&b)e.push(new w.Paren(new w.Rule(c,b,null,null,x,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new w.Paren(b))}}while(b);return e.length>0?new w.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!j(","))break}else if(a=b.variable(),a&&(c.push(a),!j(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=s(x,u,a)),i(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new w.Media(c,b,x,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,f,g,h,k,l,m;if("@"===u.charAt(x)){if(c=this["import"]()||this.media())return c;if(d(),b=i(/^@[a-z-]+/)){switch(g=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(g="@"+b.slice(b.indexOf("-",2)+1)),g){case"@font-face":h=!0;break;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":h=!0;break;case"@host":case"@page":case"@document":case"@supports":case"@keyframes":h=!0,k=!0;break;case"@namespace":l=!0}if(k&&(m=(i(/^[^{]+/)||"").trim(),m&&(b+=" "+m)),h){if(f=this.block())return new w.Directive(b,f,x,a.currentFileInfo)}else if(c=l?this.expression():this.entity(),c&&j(";")){var n=new w.Directive(b,c,x,a.currentFileInfo);return a.dumpLineNumbers&&(n.debugInfo=s(x,u,a)),n}e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!j(",")))break;while(a);return b.length>0?new w.Value(b):void 0},important:function(){return"!"===u.charAt(x)?i(/^! *important/):void 0},sub:function(){var a,b;return j("(")&&(a=this.addition())?(b=new w.Expression([a]),m(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=g(u,x-1);;){if(o(/^\/[*\/]/))break;if(c=j("/")||j("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=g(u,x-1);;){if(c=i(/^[-+]\s+/)||!e&&(j("+")||j("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},conditions:function(){var a,b,c,d=x;if(a=this.condition()){for(;;){if(!o(/^,\s*(not\s*)?\(/)||!j(","))break;if(b=this.condition(),!b)break;c=new w.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=x,g=!1;return i(/^not/)&&(g=!0),m("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=i(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new w.Condition(d,a,b,f,g):n("expected expression")):c=new w.Condition("=",a,new w.Keyword("true"),f,g),m(")"),i(/^and/)?new w.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=u.charAt(x+1);"-"!==u.charAt(x)||"@"!==c&&"("!==c||(a=j("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new w.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),o(/^\/[\/*]/)||(b=j("/"),b&&c.push(new w.Anonymous(b))));while(a);return c.length>0?new w.Expression(c):void 0},property:function(){var a=i(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(c);return b?(e.push(x+f),f+=b[0].length,c=c.slice(b[1].length),d.push(b[1])):void 0}var c=D,d=[],e=[],f=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(d.length>1&&b(/^\s*(\+?)\s*:/)){k(f);for(var g in d)"@"===d[g].charAt(0)&&(d[g]=new w.Variable("@"+d[g].slice(2,-1),e[g],a.currentFileInfo));return d}}}}},v.Parser.serializeVars=function(a){var b="";for(var c in a)if(Object.hasOwnProperty.call(a,c)){var d=a[c];b+=("@"===c[0]?"":"@")+c+": "+d+(";"===(""+d).slice(-1)?"":";")}return b},function(d){function e(a,b,c){if(!(c instanceof d.Dimension))throw{type:"Argument",message:"argument must be a number"};return null==b?b=c.unit:c=c.unify(),new d.Dimension(a(parseFloat(c.value)),b)}function f(a,b,c){var e,f,g,h,i=b.alpha,j=c.alpha,k=[];g=j+i*(1-j);for(var l=0;3>l;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)b[a]=e.bind(null,Math[a],l[a]);for(a in m)b[a]=f.bind(null,m[a]);a=d.defaultFunc,b.default=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(k[f]=g)):(l[j]=k.length,k.push(g))):k.push(g); -return 1==k.length?k[0]:(c=k.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q="'data:"+f+","+n+"'";return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function c(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&c();var e,f,g,h,i,j,k,l=Array.prototype.slice.call(arguments,1),m="linear",n='x="0" y="0" width="1" height="1"',o=!0,p={compress:!1},q=a.toCSS(p);switch(q){case"to bottom":e='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":e='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":e='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":e='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":m="radial",e='cx="50%" cy="50%" r="75%"',n='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(f='<'+m+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+e+">",g=0;gk?' stop-opacity="'+k+'"':"")+"/>";if(f+="',o)try{f=new Buffer(f).toString("base64")}catch(r){o=!1}return f="'data:image/svg+xml"+(o?";base64":"")+","+f+"'",new d.URL(new d.Anonymous(f))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),a.tabLevel--,void 0}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a.value||a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){return.2126*this.rgb[0]/255+.7152*this.rgb[1]/255+.0722*this.rgb[2]/255},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(a,b){var e=a&&a.compress&&!b;if(this.alpha<1)return 0===this.alpha&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(this.alpha,1)).join(","+(e?"":" "))+")";var f=this.toRGB();if(e){var g=f.split("");g[1]===g[2]&&g[3]===g[4]&&g[5]===g[6]&&(f="#"+g[1]+g[3]+g[5])}return f},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(b){if(a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.value,d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&this.unit.isLength())return b.add(d),void 0;c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset(null,c)],this.rules[0].allowImports=!0):this.value=c,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules&&(this.rules=a.visitArray(this.rules)),this.value&&(this.value=a.visit(this.value))},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|"},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip)return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}for(var h in b.frames[0].variables())e[h.slice(1)]={value:b.frames[0].variables()[h].value,toJS:function(){return this.value.eval(b).toCSS()}};try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo);return[new a.Selector([b],null,null,this.index,this.currentFileInfo)]},markReferenced:function(){var a,b=this.rules[0].rules;for(this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)q.value(h),p[h]=d.matchCondition(e,b);if(p[0]||p[1]){if(p[0]!=p[1]){if(r)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};r=!0,l.matchIfDefault=!0,l.matchIfDefaultValue=p[1]}o.push(l)}}else o.push(l);n=!0}}q.reset();for(g in o)if(l=o[g],!l.matchIfDefault||l.matchIfDefaultValue==(1==o.length))try{d=l.mixin,d instanceof a.mixin.Definition||(d=new a.mixin.Definition("",[],d.rules,null,!1),d.originalRuleset=c[g].originalRuleset||c[g]),Array.prototype.push.apply(m,d.eval(b,e,this.important).rules)}catch(s){throw{message:s.message,index:this.index,filename:this.currentFileInfo.filename,stack:s.stack}}if(n){if(!this.currentFileInfo||!this.currentFileInfo.reference)for(f=0;fthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d=!1,e=this.name.map?this.name.map(function(a){return a.eval?a.eval(c).value:a}).join(""):this.name;"font"!==e||c.strictMath||(d=!0,c.strictMath=!0);try{return new a.Rule(e,this.value.eval(c),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(f){throw f.index===b&&(f.index=this.index),f}finally{d&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f=this.selectors,g=a.defaultFunc;if(f&&(d=f.length)){for(c=[],g.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),e=0;d>e;e++)c.push(f[e].eval(b));g.reset()}var h,i=this.rules?this.rules.slice(0):null,j=new a.Ruleset(c,i,this.strictImports);j.originalRuleset=this,j.root=this.root,j.firstRoot=this.firstRoot,j.allowImports=this.allowImports,this.debugInfo&&(j.debugInfo=this.debugInfo);var k=b.frames;k.unshift(j);var l=b.selectors;l||(b.selectors=l=[]),l.unshift(this.selectors),(j.root||j.allowImports||!j.strictImports)&&j.evalImports(b);var m=j.rules,n=m?m.length:0;for(e=0;n>e;e++)m[e]instanceof a.mixin.Definition&&(m[e].frames=k.slice(0));var o=b.mediaBlocks&&b.mediaBlocks.length||0;for(e=0;n>e;e++)m[e]instanceof a.mixin.Call&&(i=m[e].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!j.variable(b.name):!0}),m.splice.apply(m,[e,1].concat(i)),n+=i.length-1,e+=i.length-1,j.resetCache());for(e=0;n>e;e++)h=m[e],h instanceof a.mixin.Definition||(m[e]=h.eval?h.eval(b):h);if(k.shift(),l.shift(),b.mediaBlocks)for(e=o;eb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",0,j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,this.condition,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e},match:function(a){var b,c,d,e=this.elements,f=e.length;if(b=a.elements.map(function(a){return a.combinator.value+(a.value.value||a.value)}).join("").match(/[,&#\.\w-]([\w-]|(\\.))*/g),!b)return 0;if("&"===b[0]&&b.shift(),c=b.length,0===c||c>f)return 0;for(d=0;c>d;d++)if(e[d].value!==b[d])return 0;return c},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d){this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),g&&!h.importMultiple&&(b.skip=g);var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};return!d||(b.root=d,b.importedFilename=i,f||b.skip)?(j(),void 0):(new a.importVisitor(e._importer,j,h).run(d),void 0)})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(){return[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */")}return this._css.join("")}}(c("./tree"));var x=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);v.env=v.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||x?"development":"production");var y={info:2,errors:1,none:0};if(v.logLevel="undefined"!=typeof v.logLevel?v.logLevel:y.info,v.async=v.async||!1,v.fileAsync=v.fileAsync||!1,v.poll=v.poll||(x?1e3:1500),v.functions)for(var z in v.functions)v.tree.functions[z]=v.functions[z];var A=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);A&&(v.dumpLineNumbers=A[1]);var B=/^text\/(x-)?less$/,C=null,D={};if(v.watch=function(){return v.watchMode||(v.env="development",u()),this.watchMode=!0,!0},v.unwatch=function(){return clearInterval(v.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&v.watch(),"development"!=v.env)try{C="undefined"==typeof a.localStorage?null:a.localStorage}catch(E){}var F=document.getElementsByTagName("link");v.sheets=[];for(var G=0;G - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = current; memo = currentPos = i; } - function restore() { current = temp; currentPos = i = memo; } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastClosing, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; continue; - case 41: // ) - parenLevel--; continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; lastOpening = parserCurrentIndex; continue; - case 125: // } - level--; lastClosing = parserCurrentIndex; - if (!level) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if (level > 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } - return fail("missing opening `{`", lastClosing); - } else if (parenLevel !== 0) { - return fail((parenLevel > 0) ? "missing closing `)`" : "missing opening `(`"); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (elements && ($char(';') || peekChar('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - while (true) { - if (isCall) { - arg = parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(parsers.expression); - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$char(')')) { - furthest = i; - restore(); - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - name = this.variable() || this.ruleProperty(); - if (name) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // a name returned by this.ruleProperty() is always an array of the form: - // ["", "string-1", ..., "string-n", ""] or ["", "string-1", ..., "string-n", "+"] - merge = name.pop && (name.pop() === "+"); - - if (value && this.end()) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($re(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) { - rules = this.block(); - if (rules) { - return new(tree.Directive)(name, rules, index, env.currentFileInfo); - } - } else { - value = hasExpression ? this.expression() : this.entity(); - if (value && $char(';')) { - var directive = new(tree.Directive)(name, value, index, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert @{var}s to tree.Variable(s) and return: - skipWhitespace(length); - for (var k in name) { - if (name[k].charAt(0) === '@') { - name[k] = new tree.Variable('@' + name[k].slice(2, -1), - index[k], env.currentFileInfo); - } - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require("fs"), - path = require("path"), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - // only works in node, needs interface to what is supported in environment - try { - returner = new Buffer(returner).toString('base64'); - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - - // 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 (this.alpha < 1) { - if (this.alpha === 0 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(this.alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)(null, value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - if (this.value) { - this.value = visitor.visit(this.value); - } - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath + pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); - return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule; - var candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, defaultUsed = false; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - defaultFunc.value(f); - conditionResult[f] = mixin.matchCondition(args, env); - } - if (conditionResult[0] || conditionResult[1]) { - if (conditionResult[0] != conditionResult[1]) { - if (defaultUsed) { - // todo: ideally, it would make sense to also print the candidate - // mixin definitions that cause the conflict (current one and the - // mixin that set defaultUsed flag). But is there any easy method - // to get their filename/line/index info here? - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - - defaultUsed = true; - candidate.matchIfDefault = true; - candidate.matchIfDefaultValue = conditionResult[1]; - } - - candidates.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m]; - if (!candidate.matchIfDefault || (candidate.matchIfDefaultValue == (candidates.length == 1))) { - try { - mixin = candidate.mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - //if (this.important) { - // isImportant = env.isImportant; - // env.isImportant = true; - //} - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - //if (this.important) { - // env.isImportant = isImportant; - //} - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false; - var name = this.name.map ? - this.name.map( function(v) { - return v.eval ? v.eval(env).value : v; - }).join('') : this.name; - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (e.index === undefined) { - e.index = this.index; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, i, defaultFunc = tree.defaultFunc; - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selectors.push(thisSelectors[i].eval(env)); - } - defaultFunc.reset(); - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition) { - rsRules[i].frames = envFrames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRuleCnt; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition)) { - rsRules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, i; - - oelements = other.elements.map( function(v) { - return v.combinator.value + (v.value.value || v.value); - }).join("").match(/[,&#\.\w-]([\w-]|(\\.))*/g); - // ^ regexp could be more simple but see test/less/css-escapes.less:17, doh! - - if (!oelements) { - return 0; - } - - if (oelements[0] === "&") { - oelements.shift(); - } - - olen = oelements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== oelements[i]) { - return 0; - } - } - } - return olen; // return number of matched elements - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple'// whether we are currently importing multiple copies - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info; - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.info); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.6.1.min.js b/dist/less-1.6.1.min.js deleted file mode 100644 index 3636c6c5b..000000000 --- a/dist/less-1.6.1.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * LESS - Leaner CSS v1.6.1 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"development"==v.env&&"undefined"!=typeof console&&v.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,y.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&C){d("saving "+f+" to cache.",y.info);try{C.setItem(f,a),C.setItem(f+":timestamp",c)}catch(k){d("failed to save",y.errors)}}}function h(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==v.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function i(a,b){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?f(a,b):"function"==typeof v.errorReporting&&v.errorReporting("add",a,b):h(a,b)}function j(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function k(){}function l(a){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?k(a):"function"==typeof v.errorReporting&&v.errorReporting("remove",a):j(a)}function m(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function o(a,b){var c,d,e,f,g=n(a),h=n(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=p(),h=x?v.fileAsync:v.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",y.info),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),x&&!v.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function r(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=n(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=v.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?n(e.rootpath+o(f.path,h.entryPath)).path:f.path),e.useFileCache&&D[g])try{var i=D[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else q(g,e.mime,function(a,b){D[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function s(a,b,c,d,e){var f=new v.tree.parseEnv(v);f.mime=a.type,(e||v.globalVars)&&(f.useFileCache=!0),r(a.href,null,function(h,i,j,k,m){if(m){m.remaining=d;var n=C&&C.getItem(j),o=C&&C.getItem(j+":timestamp");if(!c&&o&&m.lastModified&&new Date(m.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),m.local=!0,b(null,null,i,a,m,j),void 0}l(j),i?(f.currentFileInfo=k,new v.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,m,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:v.globalVars})):b(h,null,null,a,m,j)},f,e)}function t(a,b,c){for(var d=0;dE&&(D=D.slice(x-E),E=x)}function g(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function h(a){var b,c,d=typeof a;return"string"===d?u.charAt(x)!==a?null:(k(1),a):(f(),(b=a.exec(D))?(c=b[0].length,k(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function i(a){x>E&&(D=D.slice(x-E),E=x);var b=a.exec(D);return b?(k(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function j(a){return u.charAt(x)!==a?null:(k(1),a)}function k(a){for(var b,c=x,d=y,e=x-E,f=x+D.length-e,g=x+=a,h=u;f>x&&(b=h.charCodeAt(x),!(b>32))&&(32===b||10===b||9===b||13===b);x++);return D=D.slice(a+x-g+e),E=x,!D.length&&y=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function s(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==v.mode&&"rhino"!==v.mode&&(e=c("path").resolve(e)),{lineNumber:r(a,b).line+1,fileName:e}}function t(a,b){var c=q(a,b),d=r(a.index,c),e=d.line,f=d.column,g=a.call&&r(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var u,x,y,z,A,B,C,D,E,F,G,H=a&&a.filename;a instanceof w.parseEnv||(a=new w.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files||d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};v.Parser.importer?v.Parser.importer(b,c,g,a):v.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new w.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new v.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=i;return t.prototype=new Error,t.prototype.constructor=t,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,F={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(x=y=E=B=0,j=f&&f.globalVars?v.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+v.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,F.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),u=d=m+d.replace(/^\uFEFF/,"")+k,F.imports.contents[a.currentFileInfo.filename]=d,C=function(b){function c(b,c){l=new t({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++;continue;case 41:q--;continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:p--,f=i,p||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j)}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?p>0?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):c("missing opening `{`",f):0!==q?c(q>0?"missing closing `)`":"missing opening `(`"):(d(!0),r)}(d),l)return e(new t(l,a));D=C[0];try{g=new w.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new t(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new w.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof w.Value||(b instanceof w.Expression||(b=new w.Expression([b])),b=new w.Value([b])),new w.Rule("@"+a,b,!1,null,0)}),i.frames=[new w.Ruleset(null,f)]);try{var j,k=[],l=[new w.joinSelectorVisitor,new w.processExtendsVisitor,new w.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=i(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new w.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=i(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new w.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=x;return"~"===u.charAt(e)&&(e++,d=!0),"`"===u.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||n("You are using JavaScript, which has been disabled."),d&&j("~"),c=i(/^`([^`]*)`/),c?new w.JavaScript(c[1],x,d):void 0):void 0}},variable:function(){var a;return"@"===u.charAt(x)&&(a=i(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e,f,g=x;if(a?i(/^&:extend\(/):i(/^:extend\(/)){do{for(d=null,b=null;!(d=i(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new w.Extend(new w.Selector(b),d,g),e?e.push(f):e=[f]}while(j(","));return l(/^\)/),a&&l(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,f,g,h,k,l=u.charAt(x),n=!1,o=x;if("."===l||"#"===l){for(d();;){if(b=x,g=i(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!g)break;f=new w.Element(h,g,b,a.currentFileInfo),c?c.push(f):c=[f],h=j(">")}return j("(")&&(k=this.args(!0).args,m(")")),G.important()&&(n=!0),c&&(j(";")||p("}"))?new w.mixin.Call(c,k,o,a.currentFileInfo,n):(e(),void 0)}},args:function(a){for(var b,c,d,e,f,g,h=F.parsers,k=h.entities,m={args:null,variadic:!1},o=[],p=[],q=[];;){if(a)g=h.expression();else{if(h.comments(),"."===u.charAt(x)&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({variadic:!0});break}g=k.variable()||k.literal()||k.keyword()}if(!g)break;e=null,g.throwAwayComments&&g.throwAwayComments(),f=g;var r=null;if(a?1==g.value.length&&(r=g.value[0]):r=g,r&&r instanceof w.Variable)if(j(":"))o.length>0&&(b&&n("Cannot mix ; and , as delimiter types"),c=!0),f=l(h.expression),e=d=r.name;else{if(!a&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({name:g.name,variadic:!0});break}a||(d=e=r.name,f=null)}f&&o.push(f),q.push({name:e,value:f}),j(",")||(j(";")||b)&&(c&&n("Cannot mix ; and , as delimiter types"),b=!0,o.length>1&&(f=new w.Value(o)),p.push({name:d,value:f}),d=null,o=[],c=!1)}return m.args=b?p:q,m},definition:function(){var a,b,c,f,g=[],h=!1;if(!("."!==u.charAt(x)&&"#"!==u.charAt(x)||o(/^[^{]*\}/))&&(d(),b=i(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=b[1];var k=this.args(!1);if(g=k.args,h=k.variadic,j(")")||(B=x,e()),G.comments(),i(/^when/)&&(f=l(G.conditions,"expected condition")),c=G.block())return new w.mixin.Definition(a,g,c,f,h);e()}}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return j(";")||p("}")},alpha:function(){var a;if(i(/^\(opacity=/i))return a=i(/^\d+/)||this.entities.variable(),a?(m(")"),new w.Alpha(a)):void 0},element:function(){var b,c,d,e=x;return c=this.combinator(),b=i(/^(?:\d+\.\d+|\d+)%/)||i(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||j("*")||j("&")||this.attribute()||i(/^\([^()@]+\)/)||i(/^[\.#](?=@)/)||this.entities.variableCurly(),b||j("(")&&(d=this.selector())&&j(")")&&(b=new w.Paren(d)),b?new w.Element(c,b,e,a.currentFileInfo):void 0},combinator:function(){var a=u.charAt(x);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(x++,"^"===u.charAt(x)&&(a="^^",x++);g(u,x);)x++;return new w.Combinator(a)}return g(u,x-1)?new w.Combinator(" "):new w.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=x,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=l(this.conditions,"expected condition"):i?n("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&n("Extend can only be used at the end of selector"),e=u.charAt(x),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new w.Selector(c,d,i,j,a.currentFileInfo):(d&&n("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){if(j("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=l(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=i(/^[|~*$^]?=/),c&&(b=d.quoted()||i(/^[0-9]+%/)||i(/^[\w-]+/)||d.variableCurly()),m("]"),new w.Attribute(a,c,b)}},block:function(){var a;return j("{")&&(a=this.primary())&&j("}")?a:void 0},ruleset:function(){var b,c,f,g;for(d(),a.dumpLineNumbers&&(g=s(x,u,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&n("Guards are only currently allowed on a single selector."),!j(","))break;c.condition&&n("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(f=this.block())){var h=new w.Ruleset(b,f,a.strictImports);return a.dumpLineNumbers&&(h.debugInfo=g),h}B=x,e()},rule:function(b){var c,f,g,h=u.charAt(x),i=!1;if(d(),"."!==h&&"#"!==h&&"&"!==h&&(c=this.variable()||this.ruleProperty())){if(f=!b&&(a.compress||c.charAt&&"@"===c.charAt(0))?this.value()||this.anonymousValue():this.anonymousValue()||this.value(),g=this.important(),i=c.pop&&"+"===c.pop(),f&&this.end())return new w.Rule(c,f,g,i,A,a.currentFileInfo);if(B=x,e(),f&&!b)return this.rule(!0)}},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(D),a?(x+=a[0].length-1,new w.Anonymous(a[1])):void 0},"import":function(){var b,c,f=x;d();var g=i(/^@import?\s+/),h=(g?this.importOptions():null)||{};return g&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),j(";"))?(c=c&&new w.Value(c),new w.Import(b,c,h,f,a.currentFileInfo)):(e(),void 0)},importOptions:function(){var a,b,c,d={};if(!j("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!j(","))break}while(a);return m(")"),d},importOption:function(){var a=i(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(j("(")){if(c=this.property(),b=this.value(),!j(")"))return null;if(c&&b)e.push(new w.Paren(new w.Rule(c,b,null,null,x,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new w.Paren(b))}}while(b);return e.length>0?new w.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!j(","))break}else if(a=b.variable(),a&&(c.push(a),!j(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=s(x,u,a)),i(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new w.Media(c,b,x,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,f,g,h,k,l,m,n=x;if("@"===u.charAt(x)){if(c=this["import"]()||this.media())return c;if(d(),b=i(/^@[a-z-]+/)){switch(g=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(g="@"+b.slice(b.indexOf("-",2)+1)),g){case"@font-face":h=!0;break;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":h=!0;break;case"@host":case"@page":case"@document":case"@supports":case"@keyframes":h=!0,k=!0;break;case"@namespace":l=!0}if(k&&(m=(i(/^[^{]+/)||"").trim(),m&&(b+=" "+m)),h){if(f=this.block())return new w.Directive(b,f,n,a.currentFileInfo)}else if(c=l?this.expression():this.entity(),c&&j(";")){var o=new w.Directive(b,c,n,a.currentFileInfo);return a.dumpLineNumbers&&(o.debugInfo=s(x,u,a)),o}e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!j(",")))break;while(a);return b.length>0?new w.Value(b):void 0},important:function(){return"!"===u.charAt(x)?i(/^! *important/):void 0},sub:function(){var a,b;return j("(")&&(a=this.addition())?(b=new w.Expression([a]),m(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=g(u,x-1);;){if(o(/^\/[*\/]/))break;if(c=j("/")||j("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=g(u,x-1);;){if(c=i(/^[-+]\s+/)||!e&&(j("+")||j("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},conditions:function(){var a,b,c,d=x;if(a=this.condition()){for(;;){if(!o(/^,\s*(not\s*)?\(/)||!j(","))break;if(b=this.condition(),!b)break;c=new w.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=x,g=!1;return i(/^not/)&&(g=!0),m("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=i(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new w.Condition(d,a,b,f,g):n("expected expression")):c=new w.Condition("=",a,new w.Keyword("true"),f,g),m(")"),i(/^and/)?new w.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=u.charAt(x+1);"-"!==u.charAt(x)||"@"!==c&&"("!==c||(a=j("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new w.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),o(/^\/[\/*]/)||(b=j("/"),b&&c.push(new w.Anonymous(b))));while(a);return c.length>0?new w.Expression(c):void 0},property:function(){var a=i(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(c);return b?(e.push(x+f),f+=b[0].length,c=c.slice(b[1].length),d.push(b[1])):void 0}var c=D,d=[],e=[],f=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(d.length>1&&b(/^\s*(\+?)\s*:/)){k(f);for(var g in d)"@"===d[g].charAt(0)&&(d[g]=new w.Variable("@"+d[g].slice(2,-1),e[g],a.currentFileInfo));return d}}}}},v.Parser.serializeVars=function(a){var b="";for(var c in a)if(Object.hasOwnProperty.call(a,c)){var d=a[c];b+=("@"===c[0]?"":"@")+c+": "+d+(";"===(""+d).slice(-1)?"":";")}return b},function(d){function e(a,b,c){if(!(c instanceof d.Dimension))throw{type:"Argument",message:"argument must be a number"};return null==b?b=c.unit:c=c.unify(),new d.Dimension(a(parseFloat(c.value)),b)}function f(a,b,c){var e,f,g,h,i=b.alpha,j=c.alpha,k=[];g=j+i*(1-j);for(var l=0;3>l;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(k[f]=g)):(l[j]=k.length,k.push(g))):k.push(g);return 1==k.length?k[0]:(c=k.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function c(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&c();var e,f,g,h,i,j,k,l=Array.prototype.slice.call(arguments,1),m="linear",n='x="0" y="0" width="1" height="1"',o=!0,p={compress:!1},q=a.toCSS(p);switch(q){case"to bottom":e='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":e='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":e='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":e='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":m="radial",e='cx="50%" cy="50%" r="75%"',n='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(f='<'+m+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+e+">",g=0;gk?' stop-opacity="'+k+'"':"")+"/>";if(f+="',o)try{f=new Buffer(f).toString("base64")}catch(r){o=!1}return f="'data:image/svg+xml"+(o?";base64":"")+","+f+"'",new d.URL(new d.Anonymous(f))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),a.tabLevel--,void 0}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a.value||a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){return.2126*this.rgb[0]/255+.7152*this.rgb[1]/255+.0722*this.rgb[2]/255},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(a,b){var e=a&&a.compress&&!b;if(this.alpha<1)return 0===this.alpha&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(this.alpha,1)).join(","+(e?"":" "))+")";var f=this.toRGB();if(e){var g=f.split("");g[1]===g[2]&&g[3]===g[4]&&g[5]===g[6]&&(f="#"+g[1]+g[3]+g[5])}return f},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(b){if(a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.value,d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&this.unit.isLength())return b.add(d),void 0;c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset(null,c)],this.rules[0].allowImports=!0):this.value=c,this.index=d,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules&&(this.rules=a.visitArray(this.rules)),this.value&&(this.value=a.visit(this.value))},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip)return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo);return[new a.Selector([b],null,null,this.index,this.currentFileInfo)]},markReferenced:function(){var a,b=this.rules[0].rules;for(this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)q.value(h),p[h]=d.matchCondition(e,b);if(p[0]||p[1]){if(p[0]!=p[1]){if(r)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};r=!0,l.matchIfDefault=!0,l.matchIfDefaultValue=p[1]}o.push(l)}}else o.push(l);n=!0}}for(q.reset(),g=0;gthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d=!1,e=this.name.map?this.name.map(function(a){return a.eval?a.eval(c).value:a}).join(""):this.name;"font"!==e||c.strictMath||(d=!0,c.strictMath=!0);try{return new a.Rule(e,this.value.eval(c),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(f){throw f.index===b&&(f.index=this.index),f}finally{d&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f=this.selectors,g=a.defaultFunc;if(f&&(d=f.length)){for(c=[],g.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),e=0;d>e;e++)c.push(f[e].eval(b));g.reset()}var h,i=this.rules?this.rules.slice(0):null,j=new a.Ruleset(c,i,this.strictImports);j.originalRuleset=this,j.root=this.root,j.firstRoot=this.firstRoot,j.allowImports=this.allowImports,this.debugInfo&&(j.debugInfo=this.debugInfo);var k=b.frames;k.unshift(j);var l=b.selectors;l||(b.selectors=l=[]),l.unshift(this.selectors),(j.root||j.allowImports||!j.strictImports)&&j.evalImports(b);var m=j.rules,n=m?m.length:0;for(e=0;n>e;e++)m[e]instanceof a.mixin.Definition&&(m[e].frames=k.slice(0));var o=b.mediaBlocks&&b.mediaBlocks.length||0;for(e=0;n>e;e++)m[e]instanceof a.mixin.Call&&(i=m[e].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!j.variable(b.name):!0}),m.splice.apply(m,[e,1].concat(i)),n+=i.length-1,e+=i.length-1,j.resetCache());for(e=0;n>e;e++)h=m[e],h instanceof a.mixin.Definition||(m[e]=h.eval?h.eval(b):h);if(k.shift(),l.shift(),b.mediaBlocks)for(e=o;eb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",0,j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,this.condition,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e},match:function(a){var b,c,d,e=this.elements,f=e.length;if(b=a.elements.map(function(a){return a.combinator.value+(a.value.value||a.value)}).join("").match(/[,&#\.\w-]([\w-]|(\\.))*/g),!b)return 0;if("&"===b[0]&&b.shift(),c=b.length,0===c||c>f)return 0;for(d=0;c>d;d++)if(e[d].value!==b[d])return 0;return c},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d){this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),g&&!h.importMultiple&&(b.skip=g);var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};return!d||(b.root=d,b.importedFilename=i,f||b.skip)?(j(),void 0):(new a.importVisitor(e._importer,j,h).run(d),void 0)})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(){return[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */")}return this._css.join("")}}(c("./tree"));var x=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);v.env=v.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||x?"development":"production");var y={info:2,errors:1,none:0};if(v.logLevel="undefined"!=typeof v.logLevel?v.logLevel:y.info,v.async=v.async||!1,v.fileAsync=v.fileAsync||!1,v.poll=v.poll||(x?1e3:1500),v.functions)for(var z in v.functions)v.functions.hasOwnProperty(z)&&(v.tree.functions[z]=v.functions[z]);var A=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);A&&(v.dumpLineNumbers=A[1]);var B=/^text\/(x-)?less$/,C=null,D={};if(v.watch=function(){return v.watchMode||(v.env="development",u()),this.watchMode=!0,!0},v.unwatch=function(){return clearInterval(v.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&v.watch(),"development"!=v.env)try{C="undefined"==typeof a.localStorage?null:a.localStorage}catch(E){}var F=document.getElementsByTagName("link");v.sheets=[];for(var G=0;G - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = current; memo = currentPos = i; } - function restore() { current = temp; currentPos = i = memo; } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - while (true) { - if (isCall) { - arg = parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(parsers.expression); - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$char(')')) { - furthest = i; - restore(); - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - name = this.variable() || this.ruleProperty(); - if (name) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = name.pop && (name.pop().value === "+"); - - if (value && this.end()) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($re(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) { - rules = this.block(); - if (rules) { - return new(tree.Directive)(name, rules, index, env.currentFileInfo); - } - } else { - value = hasExpression ? this.expression() : this.entity(); - if (value && $char(';')) { - var directive = new(tree.Directive)(name, value, index, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p; - if (env && (env.numPrecision != null)) { - p = Math.pow(10, env.numPrecision); - return Math.round(value * p) / p; - } else { - return value; - } -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)(null, value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - if (this.value) { - this.value = visitor.visit(this.value); - } - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - e.index = e.index || this.index; - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, i, defaultFunc = tree.defaultFunc; - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selectors.push(thisSelectors[i].eval(env)); - } - defaultFunc.reset(); - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition) { - rsRules[i].frames = envFrames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - // cannot call if there is no selector, so we can just continue - if (!rule.selectors[0].evaldCondition) { - continue; - } - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info; - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.info); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.6.2.min.js b/dist/less-1.6.2.min.js deleted file mode 100644 index ff8cd55c9..000000000 --- a/dist/less-1.6.2.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * LESS - Leaner CSS v1.6.2 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"development"==v.env&&"undefined"!=typeof console&&v.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,y.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&C){d("saving "+f+" to cache.",y.info);try{C.setItem(f,a),C.setItem(f+":timestamp",c)}catch(k){d("failed to save",y.errors)}}}function h(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==v.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function i(a,b){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?f(a,b):"function"==typeof v.errorReporting&&v.errorReporting("add",a,b):h(a,b)}function j(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function k(){}function l(a){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?k(a):"function"==typeof v.errorReporting&&v.errorReporting("remove",a):j(a)}function m(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function o(a,b){var c,d,e,f,g=n(a),h=n(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=p(),h=x?v.fileAsync:v.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",y.info),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),x&&!v.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function r(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=n(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=v.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?n(e.rootpath+o(f.path,h.entryPath)).path:f.path),e.useFileCache&&D[g])try{var i=D[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else q(g,e.mime,function(a,b){D[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function s(a,b,c,d,e){var f=new v.tree.parseEnv(v);f.mime=a.type,(e||v.globalVars)&&(f.useFileCache=!0),r(a.href,null,function(h,i,j,k,m){if(m){m.remaining=d;var n=C&&C.getItem(j),o=C&&C.getItem(j+":timestamp");if(!c&&o&&m.lastModified&&new Date(m.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),m.local=!0,b(null,null,i,a,m,j),void 0}l(j),i?(f.currentFileInfo=k,new v.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,m,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:v.globalVars})):b(h,null,null,a,m,j)},f,e)}function t(a,b,c){for(var d=0;dE&&(D=D.slice(x-E),E=x)}function g(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function h(a){var b,c,d=typeof a;return"string"===d?u.charAt(x)!==a?null:(k(1),a):(f(),(b=a.exec(D))?(c=b[0].length,k(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function i(a){x>E&&(D=D.slice(x-E),E=x);var b=a.exec(D);return b?(k(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function j(a){return u.charAt(x)!==a?null:(k(1),a)}function k(a){for(var b,c=x,d=y,e=x-E,f=x+D.length-e,g=x+=a,h=u;f>x&&(b=h.charCodeAt(x),!(b>32))&&(32===b||10===b||9===b||13===b);x++);return D=D.slice(a+x-g+e),E=x,!D.length&&y=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function s(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==v.mode&&"rhino"!==v.mode&&(e=c("path").resolve(e)),{lineNumber:r(a,b).line+1,fileName:e}}function t(a,b){var c=q(a,b),d=r(a.index,c),e=d.line,f=d.column,g=a.call&&r(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var u,x,y,z,A,B,C,D,E,F,G,H=a&&a.filename;a instanceof w.parseEnv||(a=new w.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files||d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};v.Parser.importer?v.Parser.importer(b,c,g,a):v.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new w.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new v.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=i;return t.prototype=new Error,t.prototype.constructor=t,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,F={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(x=y=E=B=0,j=f&&f.globalVars?v.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+v.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,F.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),u=d=m+d.replace(/^\uFEFF/,"")+k,F.imports.contents[a.currentFileInfo.filename]=d,C=function(b){function c(b,c){l=new t({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new t(l,a));D=C[0];try{g=new w.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new t(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new w.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof w.Value||(b instanceof w.Expression||(b=new w.Expression([b])),b=new w.Value([b])),new w.Rule("@"+a,b,!1,null,0)}),i.frames=[new w.Ruleset(null,f)]);try{var j,k=[],l=[new w.joinSelectorVisitor,new w.processExtendsVisitor,new w.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=i(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new w.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=i(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new w.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=x;return"~"===u.charAt(e)&&(e++,d=!0),"`"===u.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||n("You are using JavaScript, which has been disabled."),d&&j("~"),c=i(/^`([^`]*)`/),c?new w.JavaScript(c[1],x,d):void 0):void 0}},variable:function(){var a;return"@"===u.charAt(x)&&(a=i(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e,f,g=x;if(a?i(/^&:extend\(/):i(/^:extend\(/)){do{for(d=null,b=null;!(d=i(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new w.Extend(new w.Selector(b),d,g),e?e.push(f):e=[f]}while(j(","));return l(/^\)/),a&&l(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,f,g,h,k,l=u.charAt(x),n=!1,o=x;if("."===l||"#"===l){for(d();;){if(b=x,g=i(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!g)break;f=new w.Element(h,g,b,a.currentFileInfo),c?c.push(f):c=[f],h=j(">")}return c&&(j("(")&&(k=this.args(!0).args,m(")")),G.important()&&(n=!0),G.end())?new w.mixin.Call(c,k,o,a.currentFileInfo,n):(e(),void 0)}},args:function(a){for(var b,c,d,e,f,g,h=F.parsers,k=h.entities,m={args:null,variadic:!1},o=[],p=[],q=[];;){if(a)g=h.expression();else{if(h.comments(),"."===u.charAt(x)&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({variadic:!0});break}g=k.variable()||k.literal()||k.keyword()}if(!g)break;e=null,g.throwAwayComments&&g.throwAwayComments(),f=g;var r=null;if(a?1==g.value.length&&(r=g.value[0]):r=g,r&&r instanceof w.Variable)if(j(":"))o.length>0&&(b&&n("Cannot mix ; and , as delimiter types"),c=!0),f=l(h.expression),e=d=r.name;else{if(!a&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({name:g.name,variadic:!0});break}a||(d=e=r.name,f=null)}f&&o.push(f),q.push({name:e,value:f}),j(",")||(j(";")||b)&&(c&&n("Cannot mix ; and , as delimiter types"),b=!0,o.length>1&&(f=new w.Value(o)),p.push({name:d,value:f}),d=null,o=[],c=!1)}return m.args=b?p:q,m},definition:function(){var a,b,c,f,g=[],h=!1;if(!("."!==u.charAt(x)&&"#"!==u.charAt(x)||o(/^[^{]*\}/))&&(d(),b=i(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=b[1];var k=this.args(!1);if(g=k.args,h=k.variadic,j(")")||(B=x,e()),G.comments(),i(/^when/)&&(f=l(G.conditions,"expected condition")),c=G.block())return new w.mixin.Definition(a,g,c,f,h);e()}}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return j(";")||p("}")},alpha:function(){var a;if(i(/^\(opacity=/i))return a=i(/^\d+/)||this.entities.variable(),a?(m(")"),new w.Alpha(a)):void 0},element:function(){var b,c,d,e=x;return c=this.combinator(),b=i(/^(?:\d+\.\d+|\d+)%/)||i(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||j("*")||j("&")||this.attribute()||i(/^\([^()@]+\)/)||i(/^[\.#](?=@)/)||this.entities.variableCurly(),b||j("(")&&(d=this.selector())&&j(")")&&(b=new w.Paren(d)),b?new w.Element(c,b,e,a.currentFileInfo):void 0},combinator:function(){var a=u.charAt(x);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(x++,"^"===u.charAt(x)&&(a="^^",x++);g(u,x);)x++;return new w.Combinator(a)}return g(u,x-1)?new w.Combinator(" "):new w.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=x,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=l(this.conditions,"expected condition"):i?n("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&n("Extend can only be used at the end of selector"),e=u.charAt(x),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new w.Selector(c,d,i,j,a.currentFileInfo):(d&&n("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){if(j("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=l(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=i(/^[|~*$^]?=/),c&&(b=d.quoted()||i(/^[0-9]+%/)||i(/^[\w-]+/)||d.variableCurly()),m("]"),new w.Attribute(a,c,b)}},block:function(){var a;return j("{")&&(a=this.primary())&&j("}")?a:void 0},ruleset:function(){var b,c,f,g;for(d(),a.dumpLineNumbers&&(g=s(x,u,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&n("Guards are only currently allowed on a single selector."),!j(","))break;c.condition&&n("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(f=this.block())){var h=new w.Ruleset(b,f,a.strictImports);return a.dumpLineNumbers&&(h.debugInfo=g),h}B=x,e()},rule:function(b){var c,f,g,h=u.charAt(x),i=!1;if(d(),"."!==h&&"#"!==h&&"&"!==h&&(c=this.variable()||this.ruleProperty())){if(f=!b&&(a.compress||c.charAt&&"@"===c.charAt(0))?this.value()||this.anonymousValue():this.anonymousValue()||this.value(),g=this.important(),i=c.pop&&"+"===c.pop().value,f&&this.end())return new w.Rule(c,f,g,i,A,a.currentFileInfo);if(B=x,e(),f&&!b)return this.rule(!0)}},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(D),a?(x+=a[0].length-1,new w.Anonymous(a[1])):void 0},"import":function(){var b,c,f=x;d();var g=i(/^@import?\s+/),h=(g?this.importOptions():null)||{};return g&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),j(";"))?(c=c&&new w.Value(c),new w.Import(b,c,h,f,a.currentFileInfo)):(e(),void 0)},importOptions:function(){var a,b,c,d={};if(!j("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!j(","))break}while(a);return m(")"),d},importOption:function(){var a=i(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(j("(")){if(c=this.property(),b=this.value(),!j(")"))return null;if(c&&b)e.push(new w.Paren(new w.Rule(c,b,null,null,x,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new w.Paren(b))}}while(b);return e.length>0?new w.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!j(","))break}else if(a=b.variable(),a&&(c.push(a),!j(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=s(x,u,a)),i(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new w.Media(c,b,x,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,f,g,h,k,l,m,n=x;if("@"===u.charAt(x)){if(c=this["import"]()||this.media())return c;if(d(),b=i(/^@[a-z-]+/)){switch(g=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(g="@"+b.slice(b.indexOf("-",2)+1)),g){case"@font-face":h=!0;break;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":h=!0;break;case"@host":case"@page":case"@document":case"@supports":case"@keyframes":h=!0,k=!0;break;case"@namespace":l=!0}if(k&&(m=(i(/^[^{]+/)||"").trim(),m&&(b+=" "+m)),h){if(f=this.block())return new w.Directive(b,f,n,a.currentFileInfo)}else if(c=l?this.expression():this.entity(),c&&j(";")){var o=new w.Directive(b,c,n,a.currentFileInfo);return a.dumpLineNumbers&&(o.debugInfo=s(x,u,a)),o}e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!j(",")))break;while(a);return b.length>0?new w.Value(b):void 0},important:function(){return"!"===u.charAt(x)?i(/^! *important/):void 0},sub:function(){var a,b;return j("(")&&(a=this.addition())?(b=new w.Expression([a]),m(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=g(u,x-1);;){if(o(/^\/[*\/]/))break;if(c=j("/")||j("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=g(u,x-1);;){if(c=i(/^[-+]\s+/)||!e&&(j("+")||j("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},conditions:function(){var a,b,c,d=x;if(a=this.condition()){for(;;){if(!o(/^,\s*(not\s*)?\(/)||!j(","))break;if(b=this.condition(),!b)break;c=new w.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=x,g=!1;return i(/^not/)&&(g=!0),m("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=i(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new w.Condition(d,a,b,f,g):n("expected expression")):c=new w.Condition("=",a,new w.Keyword("true"),f,g),m(")"),i(/^and/)?new w.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=u.charAt(x+1);"-"!==u.charAt(x)||"@"!==c&&"("!==c||(a=j("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new w.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),o(/^\/[\/*]/)||(b=j("/"),b&&c.push(new w.Anonymous(b))));while(a);return c.length>0?new w.Expression(c):void 0},property:function(){var a=i(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(x+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=D,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*(\+?)\s*:/)){for(k(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(k[f]=g)):(l[j]=k.length,k.push(g))):k.push(g);return 1==k.length?k[0]:(c=k.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c;return a&&null!=a.numPrecision?(c=Math.pow(10,a.numPrecision),Math.round(b*c)/c):b},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),a.tabLevel--,void 0}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a.value||a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){return.2126*this.rgb[0]/255+.7152*this.rgb[1]/255+.0722*this.rgb[2]/255},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return c.add(e),void 0;d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset(null,c)],this.rules[0].allowImports=!0):this.value=c,this.index=d,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules&&(this.rules=a.visitArray(this.rules)),this.value&&(this.value=a.visit(this.value))},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip)return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)s.value(h),r[h]=d.matchCondition(e,b);(r[0]||r[1])&&(r[0]!=r[1]&&(l.group=r[1]?u:v),q.push(l))}else q.push(l);p=!0}}for(s.reset(),n=[0,0,0],g=0;g0)m=v;else if(m=u,n[u]+n[v]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d=!1,e=this.name;"string"!=typeof e&&(e=1===e.length&&e[0]instanceof a.Keyword?e[0].value:b(c,e)),"font"!==e||c.strictMath||(d=!0,c.strictMath=!0);try{return new a.Rule(e,this.value.eval(c),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(f){throw f.index=f.index||this.index,f}finally{d&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f=this.selectors,g=a.defaultFunc;if(f&&(d=f.length)){for(c=[],g.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),e=0;d>e;e++)c.push(f[e].eval(b));g.reset()}var h,i,j=this.rules?this.rules.slice(0):null,k=new a.Ruleset(c,j,this.strictImports);k.originalRuleset=this,k.root=this.root,k.firstRoot=this.firstRoot,k.allowImports=this.allowImports,this.debugInfo&&(k.debugInfo=this.debugInfo);var l=b.frames;l.unshift(k);var m=b.selectors;m||(b.selectors=m=[]),m.unshift(this.selectors),(k.root||k.allowImports||!k.strictImports)&&k.evalImports(b);var n=k.rules,o=n?n.length:0;for(e=0;o>e;e++)n[e]instanceof a.mixin.Definition&&(n[e].frames=l.slice(0));var p=b.mediaBlocks&&b.mediaBlocks.length||0;for(e=0;o>e;e++)n[e]instanceof a.mixin.Call&&(j=n[e].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!k.variable(b.name):!0}),n.splice.apply(n,[e,1].concat(j)),o+=j.length-1,e+=j.length-1,k.resetCache());for(e=0;eb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d){this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),g&&!h.importMultiple&&(b.skip=g);var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};return!d||(b.root=d,b.importedFilename=i,f||b.skip)?(j(),void 0):(new a.importVisitor(e._importer,j,h).run(d),void 0)})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(){return[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */") -}return this._css.join("")}}(c("./tree"));var x=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);v.env=v.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||x?"development":"production");var y={info:2,errors:1,none:0};if(v.logLevel="undefined"!=typeof v.logLevel?v.logLevel:y.info,v.async=v.async||!1,v.fileAsync=v.fileAsync||!1,v.poll=v.poll||(x?1e3:1500),v.functions)for(var z in v.functions)v.functions.hasOwnProperty(z)&&(v.tree.functions[z]=v.functions[z]);var A=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);A&&(v.dumpLineNumbers=A[1]);var B=/^text\/(x-)?less$/,C=null,D={};if(v.watch=function(){return v.watchMode||(v.env="development",u()),this.watchMode=!0,!0},v.unwatch=function(){return clearInterval(v.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&v.watch(),"development"!=v.env)try{C="undefined"==typeof a.localStorage?null:a.localStorage}catch(E){}var F=document.getElementsByTagName("link");v.sheets=[];for(var G=0;G - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = current; memo = currentPos = i; } - function restore() { current = temp; currentPos = i = memo; } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - while (true) { - if (isCall) { - arg = parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(parsers.expression); - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$char(')')) { - furthest = i; - restore(); - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - name = this.variable() || this.ruleProperty(); - if (name) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = name.pop && (name.pop().value === "+"); - - if (value && this.end()) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($re(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) { - rules = this.block(); - if (rules) { - return new(tree.Directive)(name, rules, index, env.currentFileInfo); - } - } else { - value = hasExpression ? this.expression() : this.entity(); - if (value && $char(';')) { - var directive = new(tree.Directive)(name, value, index, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p; - if (env && (env.numPrecision != null)) { - p = Math.pow(10, env.numPrecision); - return Math.round(value * p) / p; - } else { - return value; - } -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)(null, value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - if (this.value) { - this.value = visitor.visit(this.value); - } - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - e.index = e.index || this.index; - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, i, defaultFunc = tree.defaultFunc; - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selectors.push(thisSelectors[i].eval(env)); - } - defaultFunc.reset(); - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition) { - rsRules[i].frames = envFrames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - // cannot call if there is no selector, so we can just continue - if (!rule.selectors[0].evaldCondition) { - continue; - } - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info; - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - createCSS(root.toCSS(less), sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.info); - createCSS(root.toCSS(less), sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.6.3.min.js b/dist/less-1.6.3.min.js deleted file mode 100644 index c890b8f88..000000000 --- a/dist/less-1.6.3.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * LESS - Leaner CSS v1.6.3 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"development"==v.env&&"undefined"!=typeof console&&v.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,y.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&C){d("saving "+f+" to cache.",y.info);try{C.setItem(f,a),C.setItem(f+":timestamp",c)}catch(k){d("failed to save",y.errors)}}}function h(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==v.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function i(a,b){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?f(a,b):"function"==typeof v.errorReporting&&v.errorReporting("add",a,b):h(a,b)}function j(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function k(){}function l(a){v.errorReporting&&"html"!==v.errorReporting?"console"===v.errorReporting?k(a):"function"==typeof v.errorReporting&&v.errorReporting("remove",a):j(a)}function m(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function o(a,b){var c,d,e,f,g=n(a),h=n(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=p(),h=x?v.fileAsync:v.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",y.info),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),x&&!v.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function r(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=n(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=v.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?n(e.rootpath+o(f.path,h.entryPath)).path:f.path),e.useFileCache&&D[g])try{var i=D[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else q(g,e.mime,function(a,b){D[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function s(a,b,c,d,e){var f=new v.tree.parseEnv(v);f.mime=a.type,(e||v.globalVars)&&(f.useFileCache=!0),r(a.href,null,function(h,i,j,k,m){if(m){m.remaining=d;var n=C&&C.getItem(j),o=C&&C.getItem(j+":timestamp");if(!c&&o&&m.lastModified&&new Date(m.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),m.local=!0,b(null,null,i,a,m,j),void 0}l(j),i?(f.currentFileInfo=k,new v.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,m,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:v.globalVars})):b(h,null,null,a,m,j)},f,e)}function t(a,b,c){for(var d=0;dE&&(D=D.slice(x-E),E=x)}function g(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function h(a){var b,c,d=typeof a;return"string"===d?u.charAt(x)!==a?null:(k(1),a):(f(),(b=a.exec(D))?(c=b[0].length,k(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function i(a){x>E&&(D=D.slice(x-E),E=x);var b=a.exec(D);return b?(k(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function j(a){return u.charAt(x)!==a?null:(k(1),a)}function k(a){for(var b,c=x,d=y,e=x-E,f=x+D.length-e,g=x+=a,h=u;f>x&&(b=h.charCodeAt(x),!(b>32))&&(32===b||10===b||9===b||13===b);x++);return D=D.slice(a+x-g+e),E=x,!D.length&&y=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function s(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==v.mode&&"rhino"!==v.mode&&(e=c("path").resolve(e)),{lineNumber:r(a,b).line+1,fileName:e}}function t(a,b){var c=q(a,b),d=r(a.index,c),e=d.line,f=d.column,g=a.call&&r(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var u,x,y,z,A,B,C,D,E,F,G,H=a&&a.filename;a instanceof w.parseEnv||(a=new w.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d in f.files||d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};v.Parser.importer?v.Parser.importer(b,c,g,a):v.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return g(b),void 0;var i=new w.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new v.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=i;return t.prototype=new Error,t.prototype.constructor=t,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,F={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(x=y=E=B=0,j=f&&f.globalVars?v.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+v.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,F.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),u=d=m+d.replace(/^\uFEFF/,"")+k,F.imports.contents[a.currentFileInfo.filename]=d,C=function(b){function c(b,c){l=new t({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new t(l,a));D=C[0];try{g=new w.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new t(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new w.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof w.Value||(b instanceof w.Expression||(b=new w.Expression([b])),b=new w.Value([b])),new w.Rule("@"+a,b,!1,null,0)}),i.frames=[new w.Ruleset(null,f)]);try{var j,k=[],l=[new w.joinSelectorVisitor,new w.processExtendsVisitor,new w.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=i(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new w.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=i(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new w.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=x;return"~"===u.charAt(e)&&(e++,d=!0),"`"===u.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||n("You are using JavaScript, which has been disabled."),d&&j("~"),c=i(/^`([^`]*)`/),c?new w.JavaScript(c[1],x,d):void 0):void 0}},variable:function(){var a;return"@"===u.charAt(x)&&(a=i(/^(@[\w-]+)\s*:/))?a[1]:void 0},extend:function(a){var b,c,d,e,f,g=x;if(a?i(/^&:extend\(/):i(/^:extend\(/)){do{for(d=null,b=null;!(d=i(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new w.Extend(new w.Selector(b),d,g),e?e.push(f):e=[f]}while(j(","));return l(/^\)/),a&&l(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,f,g,h,k,l=u.charAt(x),n=!1,o=x;if("."===l||"#"===l){for(d();;){if(b=x,g=i(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!g)break;f=new w.Element(h,g,b,a.currentFileInfo),c?c.push(f):c=[f],h=j(">")}return c&&(j("(")&&(k=this.args(!0).args,m(")")),G.important()&&(n=!0),G.end())?new w.mixin.Call(c,k,o,a.currentFileInfo,n):(e(),void 0)}},args:function(a){for(var b,c,d,e,f,g,h=F.parsers,k=h.entities,m={args:null,variadic:!1},o=[],p=[],q=[];;){if(a)g=h.expression();else{if(h.comments(),"."===u.charAt(x)&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({variadic:!0});break}g=k.variable()||k.literal()||k.keyword()}if(!g)break;e=null,g.throwAwayComments&&g.throwAwayComments(),f=g;var r=null;if(a?1==g.value.length&&(r=g.value[0]):r=g,r&&r instanceof w.Variable)if(j(":"))o.length>0&&(b&&n("Cannot mix ; and , as delimiter types"),c=!0),f=l(h.expression),e=d=r.name;else{if(!a&&i(/^\.{3}/)){m.variadic=!0,j(";")&&!b&&(b=!0),(b?p:q).push({name:g.name,variadic:!0});break}a||(d=e=r.name,f=null)}f&&o.push(f),q.push({name:e,value:f}),j(",")||(j(";")||b)&&(c&&n("Cannot mix ; and , as delimiter types"),b=!0,o.length>1&&(f=new w.Value(o)),p.push({name:d,value:f}),d=null,o=[],c=!1)}return m.args=b?p:q,m},definition:function(){var a,b,c,f,g=[],h=!1;if(!("."!==u.charAt(x)&&"#"!==u.charAt(x)||o(/^[^{]*\}/))&&(d(),b=i(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/))){a=b[1];var k=this.args(!1);if(g=k.args,h=k.variadic,j(")")||(B=x,e()),G.comments(),i(/^when/)&&(f=l(G.conditions,"expected condition")),c=G.block())return new w.mixin.Definition(a,g,c,f,h);e()}}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return j(";")||p("}")},alpha:function(){var a;if(i(/^\(opacity=/i))return a=i(/^\d+/)||this.entities.variable(),a?(m(")"),new w.Alpha(a)):void 0},element:function(){var b,c,d,e=x;return c=this.combinator(),b=i(/^(?:\d+\.\d+|\d+)%/)||i(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||j("*")||j("&")||this.attribute()||i(/^\([^()@]+\)/)||i(/^[\.#](?=@)/)||this.entities.variableCurly(),b||j("(")&&(d=this.selector())&&j(")")&&(b=new w.Paren(d)),b?new w.Element(c,b,e,a.currentFileInfo):void 0},combinator:function(){var a=u.charAt(x);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(x++,"^"===u.charAt(x)&&(a="^^",x++);g(u,x);)x++;return new w.Combinator(a)}return g(u,x-1)?new w.Combinator(" "):new w.Combinator(null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=x,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=l(this.conditions,"expected condition"):i?n("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&n("Extend can only be used at the end of selector"),e=u.charAt(x),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new w.Selector(c,d,i,j,a.currentFileInfo):(d&&n("Extend must be used to extend a selector, it cannot be used on its own"),void 0)},attribute:function(){if(j("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=l(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=i(/^[|~*$^]?=/),c&&(b=d.quoted()||i(/^[0-9]+%/)||i(/^[\w-]+/)||d.variableCurly()),m("]"),new w.Attribute(a,c,b)}},block:function(){var a;return j("{")&&(a=this.primary())&&j("}")?a:void 0},ruleset:function(){var b,c,f,g;for(d(),a.dumpLineNumbers&&(g=s(x,u,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&n("Guards are only currently allowed on a single selector."),!j(","))break;c.condition&&n("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(f=this.block())){var h=new w.Ruleset(b,f,a.strictImports);return a.dumpLineNumbers&&(h.debugInfo=g),h}B=x,e()},rule:function(b){var c,f,g,h=u.charAt(x),i=!1;if(d(),"."!==h&&"#"!==h&&"&"!==h&&(c=this.variable()||this.ruleProperty())){if(f=!b&&(a.compress||c.charAt&&"@"===c.charAt(0))?this.value()||this.anonymousValue():this.anonymousValue()||this.value(),g=this.important(),i=c.pop&&"+"===c.pop().value,f&&this.end())return new w.Rule(c,f,g,i,A,a.currentFileInfo);if(B=x,e(),f&&!b)return this.rule(!0)}},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(D),a?(x+=a[0].length-1,new w.Anonymous(a[1])):void 0},"import":function(){var b,c,f=x;d();var g=i(/^@import?\s+/),h=(g?this.importOptions():null)||{};return g&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),j(";"))?(c=c&&new w.Value(c),new w.Import(b,c,h,f,a.currentFileInfo)):(e(),void 0)},importOptions:function(){var a,b,c,d={};if(!j("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!j(","))break}while(a);return m(")"),d},importOption:function(){var a=i(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(j("(")){if(c=this.property(),b=this.value(),!j(")"))return null;if(c&&b)e.push(new w.Paren(new w.Rule(c,b,null,null,x,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new w.Paren(b))}}while(b);return e.length>0?new w.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!j(","))break}else if(a=b.variable(),a&&(c.push(a),!j(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=s(x,u,a)),i(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new w.Media(c,b,x,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,f,g,h,k,l,m,n=x;if("@"===u.charAt(x)){if(c=this["import"]()||this.media())return c;if(d(),b=i(/^@[a-z-]+/)){switch(g=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(g="@"+b.slice(b.indexOf("-",2)+1)),g){case"@font-face":h=!0;break;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":h=!0;break;case"@host":case"@page":case"@document":case"@supports":case"@keyframes":h=!0,k=!0;break;case"@namespace":l=!0}if(k&&(m=(i(/^[^{]+/)||"").trim(),m&&(b+=" "+m)),h){if(f=this.block())return new w.Directive(b,f,n,a.currentFileInfo)}else if(c=l?this.expression():this.entity(),c&&j(";")){var o=new w.Directive(b,c,n,a.currentFileInfo);return a.dumpLineNumbers&&(o.debugInfo=s(x,u,a)),o}e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!j(",")))break;while(a);return b.length>0?new w.Value(b):void 0},important:function(){return"!"===u.charAt(x)?i(/^! *important/):void 0},sub:function(){var a,b;return j("(")&&(a=this.addition())?(b=new w.Expression([a]),m(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=g(u,x-1);;){if(o(/^\/[*\/]/))break;if(c=j("/")||j("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=g(u,x-1);;){if(c=i(/^[-+]\s+/)||!e&&(j("+")||j("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new w.Operation(c,[d||a,b],e),e=g(u,x-1)}return d||a}},conditions:function(){var a,b,c,d=x;if(a=this.condition()){for(;;){if(!o(/^,\s*(not\s*)?\(/)||!j(","))break;if(b=this.condition(),!b)break;c=new w.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=x,g=!1;return i(/^not/)&&(g=!0),m("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=i(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new w.Condition(d,a,b,f,g):n("expected expression")):c=new w.Condition("=",a,new w.Keyword("true"),f,g),m(")"),i(/^and/)?new w.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=u.charAt(x+1);"-"!==u.charAt(x)||"@"!==c&&"("!==c||(a=j("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new w.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),o(/^\/[\/*]/)||(b=j("/"),b&&c.push(new w.Anonymous(b))));while(a);return c.length>0?new w.Expression(c):void 0},property:function(){var a=i(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(x+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=D,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*(\+?)\s*:/)){for(k(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(k[f]=g)):(l[j]=k.length,k.push(g))):k.push(g);return 1==k.length?k[0]:(c=k.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c;return a&&null!=a.numPrecision?(c=Math.pow(10,a.numPrecision),Math.round(b*c)/c):b},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),a.tabLevel--,void 0}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a.value||a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){return.2126*this.rgb[0]/255+.7152*this.rgb[1]/255+.0722*this.rgb[2]/255},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return c.add(e),void 0;d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c=this.unify(),d=b.unify(),e=c.value,f=d.value;return f>e?-1:e>f?1:d.unit.isEmpty()||0===c.unit.compare(d.unit)?0:-1}return-1},unify:function(){return this.convertTo({length:"m",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(b,c,d,e){this.name=b,Array.isArray(c)?(this.rules=[new a.Ruleset(null,c)],this.rules[0].allowImports=!0):this.value=c,this.index=d,this.currentFileInfo=e},a.Directive.prototype={type:"Directive",accept:function(a){this.rules&&(this.rules=a.visitArray(this.rules)),this.value&&(this.value=a.visit(this.value))},genCSS:function(b,c){c.add(this.name,this.currentFileInfo,this.index),this.rules?a.outputRuleset(b,c,this.rules):(c.add(" "),this.value.genCSS(b,c),c.add(";"))},toCSS:a.toCSS,eval:function(b){var c=this;return this.rules&&(b.frames.unshift(this),c=new a.Directive(this.name,null,this.index,this.currentFileInfo),c.rules=[this.rules[0].eval(b)],c.rules[0].root=!0,b.frames.shift()),c},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules[0].rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip)return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)s.value(h),r[h]=d.matchCondition(e,b);(r[0]||r[1])&&(r[0]!=r[1]&&(l.group=r[1]?u:v),q.push(l))}else q.push(l);p=!0}}for(s.reset(),n=[0,0,0],g=0;g0)m=v;else if(m=u,n[u]+n[v]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d=!1,e=this.name;"string"!=typeof e&&(e=1===e.length&&e[0]instanceof a.Keyword?e[0].value:b(c,e)),"font"!==e||c.strictMath||(d=!0,c.strictMath=!0);try{return new a.Rule(e,this.value.eval(c),this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(f){throw f.index=f.index||this.index,f}finally{d&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f=this.selectors,g=a.defaultFunc;if(f&&(d=f.length)){for(c=[],g.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),e=0;d>e;e++)c.push(f[e].eval(b));g.reset()}var h,i,j=this.rules?this.rules.slice(0):null,k=new a.Ruleset(c,j,this.strictImports);k.originalRuleset=this,k.root=this.root,k.firstRoot=this.firstRoot,k.allowImports=this.allowImports,this.debugInfo&&(k.debugInfo=this.debugInfo);var l=b.frames;l.unshift(k);var m=b.selectors;m||(b.selectors=m=[]),m.unshift(this.selectors),(k.root||k.allowImports||!k.strictImports)&&k.evalImports(b);var n=k.rules,o=n?n.length:0;for(e=0;o>e;e++)n[e]instanceof a.mixin.Definition&&(n[e].frames=l.slice(0));var p=b.mediaBlocks&&b.mediaBlocks.length||0;for(e=0;o>e;e++)n[e]instanceof a.mixin.Call&&(j=n[e].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!k.variable(b.name):!0}),n.splice.apply(n,[e,1].concat(j)),o+=j.length-1,e+=j.length-1,k.resetCache());for(e=0;eb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d){this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),g&&!h.importMultiple&&(b.skip=g);var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};return!d||(b.root=d,b.importedFilename=i,f||b.skip)?(j(),void 0):(new a.importVisitor(e._importer,j,h).run(d),void 0)})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1&&(d=c[0],d.value=new a.Value(c.map(function(a){return a.value})))})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */") -}return this._css.join("")}}(c("./tree"));var x=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);v.env=v.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||x?"development":"production");var y={info:2,errors:1,none:0};if(v.logLevel="undefined"!=typeof v.logLevel?v.logLevel:y.info,v.async=v.async||!1,v.fileAsync=v.fileAsync||!1,v.poll=v.poll||(x?1e3:1500),v.functions)for(var z in v.functions)v.functions.hasOwnProperty(z)&&(v.tree.functions[z]=v.functions[z]);var A=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);A&&(v.dumpLineNumbers=A[1]);var B=/^text\/(x-)?less$/,C=null,D={};if(v.watch=function(){return v.watchMode||(v.env="development",u()),this.watchMode=!0,!0},v.unwatch=function(){return clearInterval(v.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&v.watch(),"development"!=v.env)try{C="undefined"==typeof a.localStorage?null:a.localStorage}catch(E){}var F=document.getElementsByTagName("link");v.sheets=[];for(var G=0;G */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0) { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `current` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // index of current chunk, in `input` - parser, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = chunks[j], memo = i, current = i; } - function restore() { chunks[j] = temp, i = memo, current = i; } - - function sync() { - if (i > current) { - chunks[j] = chunks[j].slice(i - current); - current = i; - } - } - function isWhitespace(c) { - // Could change to \s? - var code = c.charCodeAt(0); - return code === 32 || code === 10 || code === 9; - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var match, length; - - // - // Non-terminal - // - if (tok instanceof Function) { - return tok.call(parser.parsers); - // - // Terminal - // - // Either match a single character in the input, - // or match a regexp in the current chunk (chunk[j]). - // - } else if (typeof(tok) === 'string') { - match = input.charAt(i) === tok ? tok : null; - length = 1; - sync (); - } else { - sync (); - - if (match = tok.exec(chunks[j])) { - length = match[0].length; - } else { - return null; - } - } - - // The match is confirmed, add the match length to `i`, - // 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. - // - if (match) { - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - endIndex = i + chunks[j].length, - mem = i += length; - - while (i < endIndex) { - if (! isWhitespace(input.charAt(i))) { break; } - i++; - } - chunks[j] = chunks[j].slice(length + (i - mem)); - current = i; - - if (chunks[j].length === 0 && j < chunks.length - 1) { j++; } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - var result = $(arg); - if (! result) { - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } else { - return result; - } - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(chunks[j]); - } - } - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - return parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // call `callback` when done. - // - parse: function (str, callback) { - var root, line, lines, error = null; - - i = j = current = furthest = 0; - input = str.replace(/\r\n/g, '\n'); - - // Remove potential UTF Byte Order Mark - input = input.replace(/^\uFEFF/, ''); - - parser.imports.contents[env.currentFileInfo.filename] = input; - - // Split the input into chunks. - chunks = (function (chunks) { - var j = 0, - skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g, - comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g, - string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g, - level = 0, - match, - chunk = chunks[0], - inParam; - - for (var i = 0, c, cc; i < input.length;) { - skip.lastIndex = i; - if (match = skip.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - } - } - c = input.charAt(i); - comment.lastIndex = string.lastIndex = i; - - if (match = string.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - - if (!inParam && c === '/') { - cc = input.charAt(i + 1); - if (cc === '/' || cc === '*') { - if (match = comment.exec(input)) { - if (match.index === i) { - i += match[0].length; - chunk.push(match[0]); - continue; - } - } - } - } - - switch (c) { - case '{': - if (!inParam) { - level++; - chunk.push(c); - break; - } - /* falls through */ - case '}': - if (!inParam) { - level--; - chunk.push(c); - chunks[++j] = chunk = []; - break; - } - /* falls through */ - case '(': - if (!inParam) { - inParam = true; - chunk.push(c); - break; - } - /* falls through */ - case ')': - if (inParam) { - inParam = false; - chunk.push(c); - break; - } - /* falls through */ - default: - chunk.push(c); - } - - i++; - } - if (level !== 0) { - error = new(LessError)({ - index: i-1, - type: 'Parse', - message: (level > 0) ? "missing closing `}`" : "missing opening `{`", - filename: env.currentFileInfo.filename - }, env); - } - - return chunks.map(function (c) { return c.join(''); }); - })([[]]); - - if (error) { - return callback(new(LessError)(error, env)); - } - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)([], $(this.parsers.primary)); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits)}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: { - // - // 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. - // - primary: function () { - var node, root = []; - - while ((node = $(this.extendRule) || $(this.mixin.definition) || $(this.rule) || $(this.ruleset) || - $(this.mixin.call) || $(this.comment) || $(this.directive)) - || $(/^[\s\n]+/) || $(/^;+/)) { - node && root.push(node); - } - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($(/^\/\/.*/), true, i, env.currentFileInfo); - } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(comment = $(this.comment)) { - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++, e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - e && $('~'); - - if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - - if (nameLC === 'url') { return null; } - else { i += name.length; } - - if (nameLC === 'alpha') { - alpha_ret = $(this.alpha); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $('('); // Parse the '(' and consume whitespace. - - args = $(this.entities.arguments); - - if (! $(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (arg = $(this.entities.assignment) || $(this.expression)) { - args.push(arg); - if (! $(',')) { - break; - } - } - return args; - }, - literal: function () { - return $(this.entities.dimension) || - $(this.entities.color) || - $(this.entities.quoted) || - $(this.entities.unicodeDescriptor); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$(/^url\(/)) { - return; - } - - value = $(this.entities.quoted) || $(this.entities.variable) || - $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expect(')'); - - /*jshint eqnull:true */ - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - if (value = $(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/)) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - if (ud = $(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/)) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $('~'); } - - if (str = $(/^`([^`]*)`/)) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList = []; - - if (!$(isRule ? /^&:extend\(/ : /^:extend\(/)) { return; } - - do { - option = null; - elements = []; - while (true) { - option = $(/^(all)(?=\s*(\)|,))/); - if (option) { break; } - e = $(this.element); - if (!e) { break; } - elements.push(e); - } - - option = option && option[1]; - - extendList.push(new(tree.Extend)(new(tree.Selector)(elements), option, index)); - - } while($(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var elements = [], e, c, args, index = i, s = input.charAt(i), important = false; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) { - elements.push(new(tree.Element)(c, e, i, env.currentFileInfo)); - c = $('>'); - } - if ($('(')) { - args = this.mixin.args.call(this, true).args; - expect(')'); - } - - args = args || []; - - if ($(this.important)) { - important = true; - } - - if (elements.length > 0 && ($(';') || peek('}'))) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - - restore(); - }, - args: function (isCall) { - var expressions = [], argsSemiColon = [], isSemiColonSeperated, argsComma = [], expressionContainsNamed, name, nameLoop, value, arg, - returner = {args:null, variadic: false}; - while (true) { - if (isCall) { - arg = $(this.expression); - } else { - $(this.comments); - if (input.charAt(i) === '.' && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = $(this.entities.variable) || $(this.entities.literal) - || $(this.entities.keyword); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(this.expression); - nameLoop = (name = val.name); - } else if (!isCall && $(/^\.{3}/)) { - returner.variadic = true; - if ($(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($(',')) { - continue; - } - - if ($(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) { - name = match[1]; - - var argInfo = this.mixin.args.call(this, false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$(')')) { - furthest = i; - restore(); - } - - $(this.comments); - - if ($(/^when/)) { // Guard - cond = expect(this.conditions, 'expected condition'); - } - - ruleset = $(this.block); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) || - $(this.entities.call) || $(this.entities.keyword) ||$(this.entities.javascript) || - $(this.comment); - }, - - // - // 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. - // - end: function () { - return $(';') || peek('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $(/^\(opacity=/i)) { return; } - if (value = $(/^\d+/) || $(this.entities.variable)) { - expect(')'); - return new(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 `*`. - // - element: function () { - var e, c, v; - - c = $(this.combinator); - - e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $('*') || $('&') || $(this.attribute) || $(/^\([^()@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly); - - if (! e) { - if ($('(')) { - if ((v = ($(this.selector))) && - $(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, i, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|') { - i++; - while (input.charAt(i).match(/\s/)) { i++; } - return new(tree.Combinator)(c); - } else if (input.charAt(i - 1).match(/\s/)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var e, elements = [], c, extend, extendList = [], when, condition; - - while ((isLess && (extend = $(this.extend))) || (isLess && (when = $(/^when/))) || (e = $(this.element))) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - extendList.push.apply(extendList, extend); - } else { - if (extendList.length) { - error("Extend can only be used at the end of selector"); - } - c = input.charAt(i); - elements.push(e); - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements.length > 0) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); } - if (extendList.length) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - var key, val, op; - - if (! $('[')) { return; } - - if (!(key = $(this.entities.variableCurly))) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - if ((op = $(/^[|~*$^]?=/))) { - val = $(this.entities.quoted) || $(/^[0-9]+%/) || $(/^[\w-]+/) || $(this.entities.variableCurly); - } - - expect(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($('{') && (content = $(this.primary)) && $('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors = [], s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (s = $(this.lessSelector)) { - selectors.push(s); - $(this.comments); - if (! $(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - $(this.comments); - } - - if (selectors.length > 0 && (rules = $(this.block))) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - if (name = $(this.variable) || $(this.ruleProperty)) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt(0) === '@')) ? - ($(this.value) || $(this.anonymousValue)) : - ($(this.anonymousValue) || $(this.value)); - - - important = $(this.important); - if (name[name.length-1] === "+") { - merge = true; - name = name.substr(0, name.length - 1); - } - - if (value && $(this.end)) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - if (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j])) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $(/^@import?\s+/); - - var options = (dir ? $(this.importOptions) : null) || {}; - - if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) { - features = $(this.mediaFeatures); - if ($(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $('(')) { return null; } - do { - if (o = $(this.importOption)) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $(',')) { break; } - } - } while (o); - expect(')'); - return options; - }, - - importOption: function() { - var opt = $(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var e, p, nodes = []; - - do { - if (e = ($(this.entities.keyword) || $(this.entities.variable))) { - nodes.push(e); - } else if ($('(')) { - p = $(this.property); - e = $(this.value); - if ($(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var e, features = []; - - do { - if (e = $(this.mediaFeature)) { - features.push(e); - if (! $(',')) { break; } - } else if (e = $(this.entities.variable)) { - features.push(e); - if (! $(',')) { break; } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($(/^@media/)) { - features = $(this.mediaFeatures); - - if (rules = $(this.block)) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - if (value = $(this['import']) || $(this.media)) { - return value; - } - - save(); - - name = $(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) - { - if (rules = $(this.block)) { - return new(tree.Directive)(name, rules, i, env.currentFileInfo); - } - } else { - if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) { - var directive = new(tree.Directive)(name, value, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - while (e = $(this.expression)) { - expressions.push(e); - if (! $(',')) { break; } - } - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($('(')) { - if (a = $(this.addition)) { - e = new(tree.Expression)([a]); - expect(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.operand)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*')))) { - if (a = $(this.operand)) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } else { - break; - } - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - if (m = $(this.multiplication)) { - isSpaced = isWhitespace(input.charAt(i - 1)); - while ((op = $(/^[-+]\s+/) || (!isSpaced && ($('+') || $('-')))) && - (a = $(this.multiplication))) { - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input.charAt(i - 1)); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - if (a = $(this.condition)) { - while (peek(/^,\s*(not\s*)?\(/) && $(',') && (b = $(this.condition))) { - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var a, b, c, op, index = i, negate = false; - - if ($(/^not/)) { negate = true; } - expect('('); - if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - if (op = $(/^(?:>=|<=|=<|[<=>])/)) { - if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expect(')'); - return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var negate, p = input.charAt(i + 1); - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-'); } - var o = $(this.sub) || $(this.entities.dimension) || - $(this.entities.color) || $(this.entities.variable) || - $(this.entities.call); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var e, delim, entities = []; - - while (e = $(this.addition) || $(this.entity)) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/) && (delim = $('/'))) { - entities.push(new(tree.Anonymous)(delim)); - } - } - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/)) { - return name[1]; - } - }, - ruleProperty: function () { - var name; - - if (name = $(/^(\*?-?[_a-zA-Z0-9-]+)\s*(\+?)\s*:/)) { - return name[1] + (name[2] || ""); - } - } - } - }; -}; - - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if ((color.luma() * color.alpha) < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return this._math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _math: function (fn, unit, n) { - if (n instanceof tree.Dimension) { - /*jshint eqnull:true */ - return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit); - } else if (typeof(n) === 'number') { - return fn(n); - } else { - throw { type: "Argument", message: "argument must be a number" }; - } - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "'data:" + mimetype + ',' + buf + "'"; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"}, - {name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""}, - {name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}], - createMathFunction = function(name, unit) { - return function(n) { - /*jshint eqnull:true */ - if (unit != null) { - n = n.unify(); - } - return this._math(Math[name], unit, n); - }; - }; - -for(var i = 0; i < mathFunctions.length; i++) { - tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit); -} - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -function colorBlendInit() { - for (var f in colorBlendMode) { - tree.functions[f] = colorBlend.bind(null, colorBlendMode[f]); - } -} colorBlendInit(); - -// ~ End of Color Blending - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - if (r = fun.call(obj, obj[i])) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - output.add((env.compress ? '{' : ' {\n')); - env.tabLevel = (env.tabLevel || 0) + 1; - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); - for(var i = 0; i < rules.length; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - output.add(env.compress ? '' : '\n'); - } - env.tabLevel--; - output.add(tabSetStr + "}"); -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { return this; }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - this.args = visitor.visit(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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - /*jshint eqnull:true */ - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress; - - // 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 (this.alpha < 1.0) { - if (this.alpha === 0 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return Math.round(c); - }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return '#' + this.rgb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - var argb = [Math.round(this.alpha * 255)].concat(this.rgb); - return '#' + argb.map(function (i) { - i = Math.round(i); - i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16); - return i.length === 1 ? '0' + i : i; - }).join(''); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = this.value, - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)([], value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - this.rules = visitor.visit(this.rules); - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - this.combinator = visitor.visit(this.combinator); - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - 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; - } -}; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.path = visitor.visit(this.path); - if (!this.options.inline) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)([], this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - for (var k in env.frames[0].variables()) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: env.frames[0].variables()[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - this.features = visitor.visit(this.features); - this.rules = visitor.visit(this.rules); - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)([], [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo); - return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = args; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - this.arguments = visitor.visit(this.arguments); - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - if (mixin.matchArgs(args, env)) { - if (!mixin.matchCondition || mixin.matchCondition(args, env)) { - try { - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - //if (this.important) { - // isImportant = env.isImportant; - // env.isImportant = true; - //} - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - //if (this.important) { - // env.isImportant = isImportant; - //} - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - match = true; - } - } - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + - this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")`", - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - this.params = visitor.visit(this.params); - this.rules = visitor.visit(this.rules); - this.condition = visitor.visit(this.condition); - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, []), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.rules.unshift(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false; - if (this.name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(this.name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (e.index === undefined) { - e.index = this.index; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - for(var i = 0; i < this.paths.length; i++) { - this.paths[i] = visitor.visit(this.paths[i]); - } - } else { - this.selectors = visitor.visit(this.selectors); - } - this.rules = visitor.visit(this.rules); - }, - eval: function (env) { - var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env); }); - var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports); - var rules; - var rule; - var i; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - env.frames.unshift(ruleset); - - // currrent selectors - if (!env.selectors) { - env.selectors = []; - } - env.selectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - for (i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Definition) { - ruleset.rules[i].frames = env.frames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < ruleset.rules.length; i++) { - if (ruleset.rules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = ruleset.rules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules)); - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < ruleset.rules.length; i++) { - rule = ruleset.rules[i]; - - if (! (rule instanceof tree.mixin.Definition)) { - ruleset.rules[i] = rule.eval ? rule.eval(env) : rule; - } - } - - // Pop the stack - env.frames.shift(); - env.selectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var i, rules; - for (i = 0; i < this.rules.length; i++) { - if (this.rules[i] instanceof tree.Import) { - rules = this.rules[i].eval(env); - if (typeof rules.length === "number") { - this.rules.splice.apply(this.rules, [i, 1].concat(rules)); - i+= rules.length-1; - } else { - this.rules.splice(i, 1, rules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (this._variables) { return this._variables; } - else { - return this._variables = this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - return this.rules.filter(function (r) { - return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition); - }); - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - if (match = selector.match(rule.selectors[j])) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - return this._lookups[key] = rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - debugInfo, // Line number debugging - rule, - firstRuleset = true, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "); - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - for(i = 0; i < this.paths.length; i++) { - path = this.paths[i]; - env.firstSelector = true; - for(j = 0; j < path.length; j++) { - path[j].genCSS(env, output); - env.firstSelector = false; - } - if (i + 1 < this.paths.length) { - output.add(env.compress ? ',' : (',\n' + tabSetStr)); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - for (i = 0; i < rulesetNodes.length; i++) { - if (ruleNodes.length && firstRuleset) { - output.add((env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr)); - } - if (!firstRuleset) { - output.add((env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr)); - } - firstRuleset = false; - rulesetNodes[i].genCSS(env, output); - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList || []; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - this.elements = visitor.visit(this.elements); - this.extendList = visitor.visit(this.extendList); - this.condition = visitor.visit(this.condition); - }, - createDerived: function(elements, extendList, evaldCondition) { - /*jshint eqnull:true */ - evaldCondition = evaldCondition != null ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - oelements, olen, max, i; - - oelements = other.elements.slice( - (other.elements.length && other.elements[0].value === "&") ? 1 : 0); - olen = oelements.length; - max = Math.min(len, olen); - - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < max; i++) { - if (elements[i].value !== oelements[i].value) { - return 0; - } - } - } - return max; // return number of matched selectors - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env); - - return this.createDerived(this.elements.map(function (e) { - return e.eval(env); - }), this.extendList.map(function(extend) { - return extend.eval(env); - }), evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo) { - this.value = val; - this.currentFileInfo = currentFileInfo; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), rootpath; - - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - return new(tree.URL)(val, null); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, v, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - if (variable = tree.find(env.frames, function (frame) { - if (v = frame.variable(name)) { - return v.value.eval(env); - } - })) { - this.evaluating = false; - return variable; - } - else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // browser-only, contents of all the files - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple'// whether we are currently importing multiple copies - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.visitor = function(implementation) { - this._implementation = implementation; - }; - - tree.visitor.prototype = { - visit: function(node) { - - if (node instanceof Array) { - return this.visitArray(node); - } - - if (!node || !node.type) { - return node; - } - - var funcName = "visit" + node.type, - func = this._implementation[funcName], - visitArgs, newNode; - if (func) { - visitArgs = {visitDeeper: true}; - newNode = func.call(this._implementation, node, visitArgs); - if (this._implementation.isReplacing) { - node = newNode; - } - } - if ((!visitArgs || visitArgs.visitDeeper) && node && node.accept) { - node.accept(this); - } - funcName = funcName + "Out"; - if (this._implementation[funcName]) { - this._implementation[funcName](node); - } - return node; - }, - visitArray: function(nodes) { - var i, newNodes = []; - for(i = 0; i < nodes.length; i++) { - var evald = this.visit(nodes[i]); - if (evald instanceof Array) { - evald = this.flatten(evald); - newNodes = newNodes.concat(evald); - } else { - newNodes.push(evald); - } - } - if (this._implementation.isReplacing) { - return newNodes; - } - return nodes; - }, - doAccept: function (node) { - node.accept(this); - }, - flatten: function(arr, master) { - return arr.reduce(this.flattenReduce.bind(this), master || []); - }, - flattenReduce: function(sum, element) { - if (element instanceof Array) { - sum = this.flatten(element, sum); - } else { - sum.push(element); - } - return sum; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - var paths = []; - this.contexts.push(paths); - - if (! rulesetNode.root) { - rulesetNode.selectors = rulesetNode.selectors.filter(function(selector) { return selector.getIsOutput(); }); - if (rulesetNode.selectors.length === 0) { - rulesetNode.rules.length = 0; - } - rulesetNode.joinSelectors(paths, context, rulesetNode.selectors); - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - - // Compile rules and rulesets - for (var i = 0; i < rulesetNode.rules.length; i++) { - rule = rulesetNode.rules[i]; - - if (rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - rulesetNode.rules.splice(i, 1); - i--; - continue; - } - } - // accept the visitor to remove rules and refactor itself - // then we can decide now whether we want it or not - if (rulesetNode.rules.length > 0) { - rulesetNode.accept(this._visitor); - } - visitArgs.visitDeeper = false; - - this._mergeRules(rulesetNode.rules); - this._removeDuplicateRules(rulesetNode.rules); - - // now decide whether we keep the ruleset - if (rulesetNode.rules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || rulesetNode.rules.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - parts = groups[key] = []; - } else { - rules.splice(i--, 1); - } - - parts.push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - for(i = 0; i < rulesetNode.rules.length; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rulesetNode.rules[i]); - 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 - for(i = 0; i < rulesetNode.paths.length; i++) { - var selectorPath = rulesetNode.paths[i], - selector = selectorPath[selectorPath.length-1]; - extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if (this.inInheritanceChain(targetExtend, extend)) { continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parents = [targetExtend, extend]; - - // 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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - inInheritanceChain: function (possibleParent, possibleChild) { - if (possibleParent === possibleChild) { - return true; - } - if (possibleChild.parents) { - if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) { - return true; - } - if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) { - return true; - } - } - return false; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath || selectorPath[selectorPath.length-1].extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - this._sourceMapBasepath = options.sourceMapBasepath; - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename.replace(/\\/g, '/'); - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename].substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), this._contentsMap[filename]); - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -/*global name:true, less, loadStyleSheet, initRhinoTest, os */ - -if (typeof initRhinoTest === 'function') { // definition of additional test functions (see rhino/test-header.js) - initRhinoTest(); -} - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - ctx.filename && (message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey')); - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - print(formatError(ctx, options)); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - - args = args.filter(function (arg) { - var match; - - if (match = arg.match(/^-I(.+)$/)) { - options.paths.push(match[1]); - return false; - } - - if (match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i)) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-inline': - options.outputSourceFiles = true; - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); - options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - if (!output) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = readFile(name, 'utf-8'); - - if (!input) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } - console.log("done"); -}(arguments)); diff --git a/dist/less-rhino-1.6.2.js b/dist/less-rhino-1.6.2.js deleted file mode 100644 index e9e783972..000000000 --- a/dist/less-rhino-1.6.2.js +++ /dev/null @@ -1,9017 +0,0 @@ -/* LESS.js v1.6.2 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0) { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = current; memo = currentPos = i; } - function restore() { current = temp; currentPos = i = memo; } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - while (true) { - if (isCall) { - arg = parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(parsers.expression); - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$char(')')) { - furthest = i; - restore(); - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - name = this.variable() || this.ruleProperty(); - if (name) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = name.pop && (name.pop().value === "+"); - - if (value && this.end()) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($re(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) { - rules = this.block(); - if (rules) { - return new(tree.Directive)(name, rules, index, env.currentFileInfo); - } - } else { - value = hasExpression ? this.expression() : this.entity(); - if (value && $char(';')) { - var directive = new(tree.Directive)(name, value, index, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p; - if (env && (env.numPrecision != null)) { - p = Math.pow(10, env.numPrecision); - return Math.round(value * p) / p; - } else { - return value; - } -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)(null, value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - if (this.value) { - this.value = visitor.visit(this.value); - } - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - e.index = e.index || this.index; - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, i, defaultFunc = tree.defaultFunc; - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selectors.push(thisSelectors[i].eval(env)); - } - defaultFunc.reset(); - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition) { - rsRules[i].frames = envFrames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - // cannot call if there is no selector, so we can just continue - if (!rule.selectors[0].evaldCondition) { - continue; - } - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/less-rhino-1.6.3.js b/dist/less-rhino-1.6.3.js deleted file mode 100644 index 69cf4a354..000000000 --- a/dist/less-rhino-1.6.3.js +++ /dev/null @@ -1,9020 +0,0 @@ -/* LESS.js v1.6.3 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0) { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - temp, // temporarily holds a chunk's state, for backtracking - memo, // temporarily holds `i`, when backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { temp = current; memo = currentPos = i; } - function restore() { current = temp; currentPos = i = memo; } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - while (true) { - if (isCall) { - arg = parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - error("Cannot mix ; and , as delimiter types"); - } - expressionContainsNamed = true; - } - value = expect(parsers.expression); - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(false); - params = argInfo.args; - variadic = argInfo.variadic; - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. so we have to be nice and restore - if (!$char(')')) { - furthest = i; - restore(); - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - } - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, c = input.charAt(i), important, merge = false; - save(); - - if (c === '.' || c === '#' || c === '&') { return; } - - name = this.variable() || this.ruleProperty(); - if (name) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = name.pop && (name.pop().value === "+"); - - if (value && this.end()) { - return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasBlock, hasIdentifier, hasExpression, identifier; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1); - } - - switch(nonVendorSpecificName) { - case "@font-face": - hasBlock = true; - break; - 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 "@host": - case "@page": - case "@document": - case "@supports": - case "@keyframes": - hasBlock = true; - hasIdentifier = true; - break; - case "@namespace": - hasExpression = true; - break; - } - - if (hasIdentifier) { - identifier = ($re(/^[^{]+/) || '').trim(); - if (identifier) { - name += " " + identifier; - } - } - - if (hasBlock) { - rules = this.block(); - if (rules) { - return new(tree.Directive)(name, rules, index, env.currentFileInfo); - } - } else { - value = hasExpression ? this.expression() : this.entity(); - if (value && $char(';')) { - var directive = new(tree.Directive)(name, value, index, env.currentFileInfo); - if (env.dumpLineNumbers) { - directive.debugInfo = getDebugInfo(i, input, env); - } - return directive; - } - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - '%': function (quoted /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - str = quoted.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - str = str.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - str = str.replace(/%%/g, '%'); - return new(tree.Quoted)('"' + str + '"', str); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - return new(tree.Dimension)(val.value, unit ? unit.toCSS() : ""); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - case 1: return args[0]; - } - var i, j, current, currentUnified, referenceUnified, unit, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - order.push(current); - continue; - } - currentUnified = current.unify(); - unit = currentUnified.unit.toString(); - j = values[unit]; - if (j === undefined) { - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }) - .join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p; - if (env && (env.numPrecision != null)) { - p = Math.pow(10, env.numPrecision); - return Math.round(value * p) / p; - } else { - return value; - } -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a = this.unify(), b = other.unify(), - aValue = a.value, bValue = b.value; - - if (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) { - return -1; - } - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'm', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, index, currentFileInfo) { - this.name = name; - - if (Array.isArray(value)) { - this.rules = [new(tree.Ruleset)(null, value)]; - this.rules[0].allowImports = true; - } else { - this.value = value; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - -}; -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - if (this.value) { - this.value = visitor.visit(this.value); - } - }, - genCSS: function (env, output) { - output.add(this.name, this.currentFileInfo, this.index); - if (this.rules) { - tree.outputRuleset(env, output, this.rules); - } else { - output.add(' '); - this.value.genCSS(env, output); - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var evaldDirective = this; - if (this.rules) { - env.frames.unshift(this); - evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo); - evaldDirective.rules = [this.rules[0].eval(env)]; - evaldDirective.rules[0].root = true; - env.frames.shift(); - } - return evaldDirective; - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules[0].rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { return []; } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.eval(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = []; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - - for(i = 0; i < args.length; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic && args) { - varargs = []; - for (j = argIndex; j < args.length; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + args.length + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < args.length; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames.concat(env.frames), - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - return new(tree.Rule)(name, - this.value.eval(env), - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - e.index = e.index || this.index; - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, i, defaultFunc = tree.defaultFunc; - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selectors.push(thisSelectors[i].eval(env)); - } - defaultFunc.reset(); - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition) { - rsRules[i].frames = envFrames.slice(0); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - // cannot call if there is no selector, so we can just continue - if (!rule.selectors[0].evaldCondition) { - continue; - } - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (imported && !env.importMultiple) { importNode.skip = imported; } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - if (!inlineCSS && !importNode.skip) { - new(tree.importVisitor)(importVisitor._importer, subFinish, env) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - - rule.value = new (tree.Value)(parts.map(function (p) { - return p.value; - })); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file From e8da09e4457bcef0e90b8e47ed0ae954678fe3da Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 13 Feb 2014 22:47:05 +0000 Subject: [PATCH 007/161] some changelog notes --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0743e383e..86e0f36b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +#2.0.0 + +2014-??-?? + + - no longer including old versions of less in the repo or npm + - not including test and gradle files in npm (Note: TODO may need to move some test framework into lib for core plugins) + - colours now output in the format they are added unless compressing, so yellow will output yellow, not its hex counterpart +TODO + - Environment Support + - Finalised Plugin Support + # 1.7.0 2014-??-?? From a9e8cc6b57ce402e644c19b434f34aa52ee28be3 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 15 Feb 2014 14:47:41 +0000 Subject: [PATCH 008/161] remove dist files introduced by merge --- dist/lessc-rhino-1.6.2.js | 449 -------------------------------------- dist/lessc-rhino-1.6.3.js | 449 -------------------------------------- 2 files changed, 898 deletions(-) delete mode 100644 dist/lessc-rhino-1.6.2.js delete mode 100644 dist/lessc-rhino-1.6.3.js diff --git a/dist/lessc-rhino-1.6.2.js b/dist/lessc-rhino-1.6.2.js deleted file mode 100644 index 73dd4d25e..000000000 --- a/dist/lessc-rhino-1.6.2.js +++ /dev/null @@ -1,449 +0,0 @@ -/* LESS.js v1.6.2 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - print(formatError(ctx, options)); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } - console.log("done"); -}(arguments)); diff --git a/dist/lessc-rhino-1.6.3.js b/dist/lessc-rhino-1.6.3.js deleted file mode 100644 index 7bb2224bb..000000000 --- a/dist/lessc-rhino-1.6.3.js +++ /dev/null @@ -1,449 +0,0 @@ -/* LESS.js v1.6.3 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - print(formatError(ctx, options)); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } - console.log("done"); -}(arguments)); From c844b022f9644ef6bbb452f6676d982aadb33f28 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 15 Feb 2014 14:49:25 +0000 Subject: [PATCH 009/161] remove unused optimization level --- benchmark/less-benchmark.js | 2 +- lib/less/browser.js | 3 --- lib/less/env.js | 1 - lib/less/lessc_helper.js | 4 ---- lib/less/parser.js | 6 ------ lib/less/rhino.js | 4 ---- 6 files changed, 1 insertion(+), 19 deletions(-) diff --git a/benchmark/less-benchmark.js b/benchmark/less-benchmark.js index ba789d52b..313472861 100644 --- a/benchmark/less-benchmark.js +++ b/benchmark/less-benchmark.js @@ -15,7 +15,7 @@ fs.readFile(file, 'utf8', function (e, data) { start = new(Date); - new(less.Parser)({ optimization: 2 }).parse(data, function (err, tree) { + new(less.Parser)().parse(data, function (err, tree) { end = new Date(); total = end - start; diff --git a/lib/less/browser.js b/lib/less/browser.js index ffcf82fc1..cbc640632 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -568,7 +568,6 @@ function loadStyleSheets(callback, reload, modifyVars) { function initRunningMode(){ if (less.env === 'development') { - less.optimization = 0; less.watchTimer = setInterval(function () { if (less.watchMode) { loadStyleSheets(function (e, root, _, sheet, env) { @@ -580,8 +579,6 @@ function initRunningMode(){ }); } }, less.poll); - } else { - less.optimization = 3; } } diff --git a/lib/less/env.js b/lib/less/env.js index 10c730295..41eabe507 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -2,7 +2,6 @@ var parseCopyProperties = [ 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) 'files', // list of files that have been imported, used for import-once 'contents', // map - filename to contents of all the files 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore diff --git a/lib/less/lessc_helper.js b/lib/less/lessc_helper.js index 76355eb9c..ce758b799 100644 --- a/lib/less/lessc_helper.js +++ b/lib/less/lessc_helper.js @@ -61,10 +61,6 @@ var lessc_helper = { console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file."); console.log(""); console.log("-------------------------- Deprecated ----------------"); - console.log(" -O0, -O1, -O2 Set the parser's optimization level. The lower"); - console.log(" the number, the less nodes it will create in the"); - console.log(" tree. This could matter for debugging, or if you"); - console.log(" want to access the individual nodes in the tree."); console.log(" --line-numbers=TYPE Outputs filename and line numbers."); console.log(" TYPE can be either 'comments', which will output"); console.log(" the debug info within comments, 'mediaquery'"); diff --git a/lib/less/parser.js b/lib/less/parser.js index 018a280f9..30635a14f 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -335,12 +335,6 @@ less.Parser = function Parser(env) { this.env = env = env || {}; - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - // // The Parser // diff --git a/lib/less/rhino.js b/lib/less/rhino.js index 0ff7aaf58..f96101115 100644 --- a/lib/less/rhino.js +++ b/lib/less/rhino.js @@ -150,7 +150,6 @@ function writeFile(filename, content) { compress: false, cleancss: false, max_line_len: -1, - optimization: 1, silent: false, verbose: false, lint: false, @@ -269,9 +268,6 @@ function writeFile(filename, content) { }); } break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; case 'line-numbers': if (checkArgFunc(arg, match[2])) { options.dumpLineNumbers = match[2]; From d8467744c520886b891b3598be8c2d8044ade54a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 15 Feb 2014 14:50:08 +0000 Subject: [PATCH 010/161] remove opimization from lessc --- bin/lessc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bin/lessc b/bin/lessc index 9c27f5f0e..4ea76b433 100755 --- a/bin/lessc +++ b/bin/lessc @@ -13,7 +13,6 @@ var options = { compress: false, cleancss: false, max_line_len: -1, - optimization: 1, silent: false, verbose: false, lint: false, @@ -143,9 +142,6 @@ args = args.filter(function (arg) { }); } break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; case 'line-numbers': if (checkArgFunc(arg, match[2])) { options.dumpLineNumbers = match[2]; From edd37294bfa4eca71e14f2084dc150a04fcad5a7 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 22 Feb 2014 16:33:52 +0000 Subject: [PATCH 011/161] Fix scope test --- test/css/scope.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/css/scope.css b/test/css/scope.css index 461950e18..47967a4f6 100644 --- a/test/css/scope.css +++ b/test/css/scope.css @@ -34,5 +34,5 @@ sub-scope-only: 'inside'; } #parentSelectorScope { - prop: #ffffff; + prop: white; } From 35d58f239221edacb6384e95757f405c62b9ab3c Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 22 Feb 2014 16:34:38 +0000 Subject: [PATCH 012/161] start abstracting out the file loader --- lib/less/environments/node.js | 103 +++++++++++++++++++++++++++++++ lib/less/index.js | 113 +++++----------------------------- 2 files changed, 120 insertions(+), 96 deletions(-) create mode 100644 lib/less/environments/node.js diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js new file mode 100644 index 000000000..71844989c --- /dev/null +++ b/lib/less/environments/node.js @@ -0,0 +1,103 @@ +var path = require('path'), + url = require('url'), + request, + fs = require('fs'), + isUrlRe = /^(?:https?:)?\/\//i; + +module.exports = { + warn: function(env, msg) { + console.warn(msg); + }, + getPath: function (env, filename) { + var j = filename.lastIndexOf('/'); + if (j < 0) { + j = filename.lastIndexOf('\\'); + } + if (j < 0) { + return ""; + } + return filename.slice(0, j + 1); + }, + isPathAbsolute: function(env, filename) { + return /^(?:[a-z-]+:|\/|\\)/.test(filename); + }, + loadFile: function(env, filename, currentDirectory, callback) { + var fullFilename, + data, + isUrl = isUrlRe.test( filename ); + + if (isUrl || isUrlRe.test(currentDirectory)) { + if (request === undefined) { + try { request = require('request'); } + catch(e) { request = null; } + } + if (!request) { + callback({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" }); + return; + } + + var urlStr = isUrl ? filename : url.resolve(currentDirectory, filename); + + request.get({uri: urlStr, strictSSL: !env.insecure }, function (error, res, body) { + if (res.statusCode === 404) { + callback({ type: 'File', message: "resource '" + urlStr + "' was not found\n" }); + return; + } + if (!body) { + this.warn( env, 'Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); + } + if (error) { + callback({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" }); + } + fullFilename = urlStr; + callback(null, body, fullFilename); + }); + } else { + + var paths = [currentDirectory].concat(env.paths); + paths.push('.'); + + if (env.syncImport) { + for (var i = 0; i < paths.length; i++) { + try { + fullFilename = path.join(paths[i], filename); + fs.statSync(fullFilename); + break; + } catch (e) { + fullFilename = null; + } + } + + if (!fullFilename) { + callback({ type: 'File', message: "'" + filename + "' wasn't found" }); + return; + } + + try { + data = fs.readFileSync(fullFilename, 'utf-8'); + callback(null, data, fullFilename); + } catch (e) { + callback(e); + } + } else { + (function tryPathIndex(i) { + if (i < paths.length) { + fullFilename = path.join(paths[i], filename); + fs.stat(fullFilename, function (err) { + if (err) { + tryPathIndex(i + 1); + } else { + fs.readFile(fullFilename, 'utf-8', function(e, data) { + if (e) { callback(e); } + callback(null, data, fullFilename); + }); + } + }); + } else { + callback({ type: 'File', message: "'" + filename + "' wasn't found" }); + } + }(0)); + } + } + } +}; \ No newline at end of file diff --git a/lib/less/index.js b/lib/less/index.js index a18b81790..7a9bbaa37 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,8 +1,3 @@ -var path = require('path'), - url = require('url'), - request, - fs = require('fs'); - var less = { version: [1, 6, 3], Parser: require('./parser').Parser, @@ -123,20 +118,21 @@ require('./tree/negative'); require('./tree/extend'); require('./tree/ruleset-call'); +less.Parser.environment = require("./environments/node"); -var isUrlRe = /^(?:https?:)?\/\//i; - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - var pathname, dirname, data, - newFileInfo = { +less.Parser.fileLoader = function (filename, currentFileInfo, callback, env) { + var newFileInfo = { relativeUrls: env.relativeUrls, entryPath: currentFileInfo.entryPath, rootpath: currentFileInfo.rootpath, rootFilename: currentFileInfo.rootFilename }; - - function handleDataAndCallCallback(data) { - var j = file.lastIndexOf('/'); + + function handleDataAndCallCallback(e, data, resolvedFilename) { + if (e) { + callback(e); + return; + } // Pass on an updated rootpath if path of imported file is relative and file // is in a (sub|sup) directory @@ -146,92 +142,17 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { // then rootpath should become 'less/module/nav/' // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file + var originalRelativePath = less.Parser.environment.getPath(env, filename); + if(newFileInfo.relativeUrls && !less.Parser.environment.isPathAbsolute(env, filename) && originalRelativePath) { + newFileInfo.rootpath = newFileInfo.rootpath + originalRelativePath; // append (sub|sup) directory path of imported file } - newFileInfo.currentDirectory = pathname.replace(/[^\\\/]*$/, ""); - newFileInfo.filename = pathname; + newFileInfo.currentDirectory = less.Parser.environment.getPath(env, resolvedFilename); + newFileInfo.filename = resolvedFilename; - callback(null, data, pathname, newFileInfo); + callback(null, data, resolvedFilename, newFileInfo); //TODO adjust callback } - - var isUrl = isUrlRe.test( file ); - if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) { - if (request === undefined) { - try { request = require('request'); } - catch(e) { request = null; } - } - if (!request) { - callback({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" }); - return; - } - - var urlStr = isUrl ? file : url.resolve(currentFileInfo.currentDirectory, file), - urlObj = url.parse(urlStr); - - request.get({uri: urlStr, strictSSL: !env.insecure }, function (error, res, body) { - if (res.statusCode === 404) { - callback({ type: 'File', message: "resource '" + urlStr + "' was not found\n" }); - return; - } - if (!body) { - console.error( 'Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"' ); - } - if (error) { - callback({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" }); - } - pathname = urlStr; - dirname = urlObj.protocol +'//'+ urlObj.host + urlObj.pathname.replace(/[^\/]*$/, ''); - handleDataAndCallCallback(body); - }); - } else { - - var paths = [currentFileInfo.currentDirectory].concat(env.paths); - paths.push('.'); - - if (env.syncImport) { - for (var i = 0; i < paths.length; i++) { - try { - pathname = path.join(paths[i], file); - fs.statSync(pathname); - break; - } catch (e) { - pathname = null; - } - } - - if (!pathname) { - callback({ type: 'File', message: "'" + file + "' wasn't found" }); - return; - } - try { - data = fs.readFileSync(pathname, 'utf-8'); - handleDataAndCallCallback(data); - } catch (e) { - callback(e); - } - } else { - (function tryPathIndex(i) { - if (i < paths.length) { - pathname = path.join(paths[i], file); - fs.stat(pathname, function (err) { - if (err) { - tryPathIndex(i + 1); - } else { - fs.readFile(pathname, 'utf-8', function(e, data) { - if (e) { callback(e); } - handleDataAndCallCallback(data); - }); - } - }); - } else { - callback({ type: 'File', message: "'" + file + "' wasn't found" }); - } - }(0)); - } - } + less.Parser.environment.loadFile(env, filename, currentFileInfo.currentDirectory, handleDataAndCallCallback); }; require('./env'); @@ -244,4 +165,4 @@ require('./join-selector-visitor.js'); require('./to-css-visitor.js'); require('./source-map-output.js'); -for (var k in less) { if (less.hasOwnProperty(k)) { exports[k] = less[k]; }} +module.exports = less; \ No newline at end of file From e989b01f514e5e2cc364c247ac938f1f817d28c3 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 22 Feb 2014 16:47:59 +0000 Subject: [PATCH 013/161] remove the old importer interface and move the node code into the common importer --- lib/less/index.js | 40 ++--------------------------- lib/less/parser.js | 63 ++++++++++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 59 deletions(-) diff --git a/lib/less/index.js b/lib/less/index.js index 7a9bbaa37..d1560905f 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -87,6 +87,8 @@ var less = { } }; +less.Parser.environment = require("./environments/node"); + require('./tree/color'); require('./tree/directive'); require('./tree/detached-ruleset'); @@ -117,44 +119,6 @@ require('./tree/unicode-descriptor'); require('./tree/negative'); require('./tree/extend'); require('./tree/ruleset-call'); - -less.Parser.environment = require("./environments/node"); - -less.Parser.fileLoader = function (filename, currentFileInfo, callback, env) { - var newFileInfo = { - relativeUrls: env.relativeUrls, - entryPath: currentFileInfo.entryPath, - rootpath: currentFileInfo.rootpath, - rootFilename: currentFileInfo.rootFilename - }; - - function handleDataAndCallCallback(e, data, resolvedFilename) { - if (e) { - callback(e); - return; - } - - // Pass on an updated rootpath if path of imported file is relative and file - // is in a (sub|sup) directory - // - // Examples: - // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', - // then rootpath should become 'less/module/nav/' - // - If path of imported file is '../mixins.less' and rootpath is 'less/', - // then rootpath should become 'less/../' - var originalRelativePath = less.Parser.environment.getPath(env, filename); - if(newFileInfo.relativeUrls && !less.Parser.environment.isPathAbsolute(env, filename) && originalRelativePath) { - newFileInfo.rootpath = newFileInfo.rootpath + originalRelativePath; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = less.Parser.environment.getPath(env, resolvedFilename); - newFileInfo.filename = resolvedFilename; - - callback(null, data, resolvedFilename, newFileInfo); //TODO adjust callback - } - - less.Parser.environment.loadFile(env, filename, currentFileInfo.currentDirectory, handleDataAndCallCallback); -}; - require('./env'); require('./functions'); require('./colors'); diff --git a/lib/less/parser.js b/lib/less/parser.js index 94d885851..40e3592b5 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -82,31 +82,52 @@ less.Parser = function Parser(env) { callback(e, root, importedPreviously, fullPath); }; - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} + var newFileInfo = { + relativeUrls: env.relativeUrls, + entryPath: currentFileInfo.entryPath, + rootpath: currentFileInfo.rootpath, + rootFilename: currentFileInfo.rootFilename + }; + + less.Parser.environment.loadFile(env, path, currentFileInfo.currentDirectory, function (e, contents, resolvedFilename) { + if (e) { + fileParsedFunc(e); + return; + } - var newEnv = new tree.parseEnv(env); + // Pass on an updated rootpath if path of imported file is relative and file + // is in a (sub|sup) directory + // + // Examples: + // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', + // then rootpath should become 'less/module/nav/' + // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // then rootpath should become 'less/../' + var originalRelativePath = less.Parser.environment.getPath(env, path); + if(newFileInfo.relativeUrls && !less.Parser.environment.isPathAbsolute(env, path) && originalRelativePath) { + newFileInfo.rootpath = newFileInfo.rootpath + originalRelativePath; // append (sub|sup) directory path of imported file + } + newFileInfo.currentDirectory = less.Parser.environment.getPath(env, resolvedFilename); + newFileInfo.filename = resolvedFilename; - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; + var newEnv = new tree.parseEnv(env); - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } + newEnv.currentFileInfo = newFileInfo; + newEnv.processImports = false; + newEnv.contents[resolvedFilename] = contents; - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } + if (currentFileInfo.reference || importOptions.reference) { + newFileInfo.reference = true; + } + + if (importOptions.inline) { + fileParsedFunc(null, contents, resolvedFilename); + } else { + new(less.Parser)(newEnv).parse(contents, function (e, root) { + fileParsedFunc(e, root, resolvedFilename); + }); + } + }); } }; From 9d94e1c914364e68a4c8fb76d587a1ab43c6060f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 22 Feb 2014 18:02:17 +0000 Subject: [PATCH 014/161] move the browser file out into the same format as the node environment file --- build/build.yml | 2 +- lib/less/browser.js | 208 ++----------------------------- lib/less/environments/api.js | 40 ++++++ lib/less/environments/browser.js | 196 +++++++++++++++++++++++++++++ lib/less/environments/node.js | 8 +- lib/less/parser.js | 2 +- 6 files changed, 251 insertions(+), 205 deletions(-) create mode 100644 lib/less/environments/api.js create mode 100644 lib/less/environments/browser.js diff --git a/build/build.yml b/build/build.yml index b5a2234a8..2dd328e1c 100644 --- a/build/build.yml +++ b/build/build.yml @@ -24,7 +24,7 @@ prepend: append: amd: build/amd.js - browser: <%= build.lib %>/browser.js + browser: ['lib/less/environments/browser.js', '<%= build.lib %>/browser.js'] rhino: <%= build.lib %>/rhino.js diff --git a/lib/less/browser.js b/lib/less/browser.js index cbc640632..ce8295235 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -1,7 +1,7 @@ // // browser.js - client-side engine // -/*global less, window, document, XMLHttpRequest, location */ +/*global less, window, document, location */ var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); @@ -54,7 +54,6 @@ if (dumpLineNumbers) { var typePattern = /^text\/(x-)?less$/; var cache = null; -var fileCache = {}; function log(str, level) { if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { @@ -323,196 +322,6 @@ function loadStyles(modifyVars) { } } -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.info); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { var env = new less.tree.parseEnv(less); @@ -522,7 +331,16 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { env.useFileCache = true; } - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { + less.Parser.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { + + var newFileInfo = { + currentDirectory: less.Parser.environment.getPath(env, path), + filename: path, + rootFilename: path, + relativeUrls: env.relativeUrls}; + + newFileInfo.entryPath = newFileInfo.currentDirectory; + newFileInfo.rootpath = less.rootpath || newFileInfo.currentDirectory; if (webInfo) { webInfo.remaining = remaining; @@ -582,8 +400,6 @@ function initRunningMode(){ } } - - // // Watch mode // @@ -656,6 +472,4 @@ less.refresh = function (reload, modifyVars) { less.refreshStyles = loadStyles; -less.Parser.fileLoader = loadFile; - less.refresh(less.env === 'development'); diff --git a/lib/less/environments/api.js b/lib/less/environments/api.js new file mode 100644 index 000000000..483895f3f --- /dev/null +++ b/lib/less/environments/api.js @@ -0,0 +1,40 @@ +module.exports = { + /** + * Warns the user about something + * @param {Object} env - the environment or options object + * @param {String} msg - the message about the warning + */ + warn: function(env, msg) { + }, + /** + * gets the path from the filename, e.g. "http://wwe.files.com/ha/ha.less" would return + * "http://wwe.files.com/ha/" + * If the filename is a file e.g. "file.less" it should return the empty string "" + * @param {Object} env - the environment or options object + * @param {String} filename - the filename to extract the path. + * @returns {String} + */ + getPath: function (env, filename) { + }, + /** + * Returns whether the path is absolute, e.g. "/file.less" = true, "file.less" = false + * @param {Object} env - the environment or options object + * @param {String} filename - the filename + * @returns {Boolean} + */ + isPathAbsolute: function(env, filename) { + }, + /** + * Loads a file for an import aynscronously (or syncronously) + * @param {Object} env - the environment or options object + * @param {String} filename - the filename + * @param {String} currentDirectory - the current directory we are in + * @param {Function} callback - a function to callback when finished, + * taking the format callback(error, contents, fullfilename, reserved) + * where error is { type: {string}, message: {string} }, contents is {string} and fullfilename is {string} + * for reserved, see browser.js which uses this argument for cache information + * @returns {Boolean} + */ + loadFile: function(env, filename, currentDirectory, callback) { + } +}; \ No newline at end of file diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js new file mode 100644 index 000000000..bb970b685 --- /dev/null +++ b/lib/less/environments/browser.js @@ -0,0 +1,196 @@ +/*global less, window, XMLHttpRequest, log, logLevel, isFileProtocol */ + +var fileCache = {}; + +//TODOS - move log somewhere. pathDiff and doing something similiar in node. use pathDiff in the other browser file for the initial load +// isFileProtocol is global + +function getXMLHttpRequest() { + if (window.XMLHttpRequest) { + return new XMLHttpRequest(); + } else { + try { + /*global ActiveXObject */ + return new ActiveXObject("MSXML2.XMLHTTP.3.0"); + } catch (e) { + log("browser doesn't support AJAX.", logLevel.errors); + return null; + } + } +} + +less.Parser.environment = { + // make generic but overriddable + warn: function warn(env, msg) { + console.warn(msg); + }, + // make generic but overriddable + getPath: function getPath(env, filename) { + var j = filename.lastIndexOf('/'); + if (j < 0) { + j = filename.lastIndexOf('\\'); + } + if (j < 0) { + return ""; + } + return filename.slice(0, j + 1); + }, + // make generic but overriddable + isPathAbsolute: function isPathAbsolute(env, filename) { + return /^(?:[a-z-]+:|\/|\\)/.test(filename); + }, + pathDiff: function pathDiff(url, baseUrl) { + // diff between two paths to create a relative path + + var urlParts = this.extractUrlParts(url), + baseUrlParts = this.extractUrlParts(baseUrl), + i, max, urlDirectories, baseUrlDirectories, diff = ""; + if (urlParts.hostPart !== baseUrlParts.hostPart) { + return ""; + } + max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); + for(i = 0; i < max; i++) { + if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } + } + baseUrlDirectories = baseUrlParts.directories.slice(i); + urlDirectories = urlParts.directories.slice(i); + for(i = 0; i < baseUrlDirectories.length-1; i++) { + diff += "../"; + } + for(i = 0; i < urlDirectories.length-1; i++) { + diff += urlDirectories[i] + "/"; + } + return diff; + }, + // helper function, not part of API + extractUrlParts: function extractUrlParts(url, baseUrl) { + // urlParts[1] = protocol&hostname || / + // urlParts[2] = / if path relative to host base + // urlParts[3] = directories + // urlParts[4] = filename + // urlParts[5] = parameters + + var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, + urlParts = url.match(urlPartsRegex), + returner = {}, directories = [], i, baseUrlParts; + + if (!urlParts) { + throw new Error("Could not parse sheet href - '"+url+"'"); + } + + // Stylesheets in IE don't always return the full path + if (!urlParts[1] || urlParts[2]) { + baseUrlParts = baseUrl.match(urlPartsRegex); + if (!baseUrlParts) { + throw new Error("Could not parse page url - '"+baseUrl+"'"); + } + urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; + if (!urlParts[2]) { + urlParts[3] = baseUrlParts[3] + urlParts[3]; + } + } + + if (urlParts[3]) { + directories = urlParts[3].replace(/\\/g, "/").split("/"); + + // extract out . before .. so .. doesn't absorb a non-directory + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".") { + directories.splice(i, 1); + i -= 1; + } + } + + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".." && i > 0) { + directories.splice(i-1, 2); + i -= 2; + } + } + } + + returner.hostPart = urlParts[1]; + returner.directories = directories; + returner.path = urlParts[1] + directories.join("/"); + returner.fileUrl = returner.path + (urlParts[4] || ""); + returner.url = returner.fileUrl + (urlParts[5] || ""); + return returner; + }, + doXHR: function doXHR(url, type, callback, errback) { + var xhr = getXMLHttpRequest(); + var async = isFileProtocol ? less.fileAsync : less.async; + + if (typeof(xhr.overrideMimeType) === 'function') { + xhr.overrideMimeType('text/css'); + } + log("XHR: Getting '" + url + "'", logLevel.info); + xhr.open('GET', url, async); + xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); + xhr.send(null); + + function handleResponse(xhr, callback, errback) { + if (xhr.status >= 200 && xhr.status < 300) { + callback(xhr.responseText, + xhr.getResponseHeader("Last-Modified")); + } else if (typeof(errback) === 'function') { + errback(xhr.status, url); + } + } + + if (isFileProtocol && !less.fileAsync) { + if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { + callback(xhr.responseText); + } else { + errback(xhr.status, url); + } + } else if (async) { + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + handleResponse(xhr, callback, errback); + } + }; + } else { + handleResponse(xhr, callback, errback); + } + }, + loadFile: function loadFile(env, filename, currentDirectory, callback) { + if (currentDirectory && !this.isPathAbsolute(env, filename)) { + filename = currentDirectory + filename; + } + + // sheet may be set to the stylesheet for the initial load or a collection of properties including + // some env variables for imports + var hrefParts = this.extractUrlParts(filename, window.location.href); + var href = hrefParts.url; + + // TODO - use pathDiff in less.node + /*if (env.relativeUrls) { + if (env.rootpath) { + newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; + } else { + newFileInfo.rootpath = hrefParts.path; + } + }*/ + + if (env.useFileCache && fileCache[href]) { + try { + var lessText = fileCache[href]; + callback(null, lessText, href, { lastModified: new Date() }); + } catch (e) { + callback(e, null, href); + } + return; + } + + this.doXHR(href, env.mime, function doXHRCallback(data, lastModified) { + // per file cache + fileCache[href] = data; + + // Use remote copy (re-parse) + callback(null, data, href, { lastModified: lastModified }); + }, function doXHRError(status, url) { + callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); + }); + + } +}; \ No newline at end of file diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js index 71844989c..2fdb36b93 100644 --- a/lib/less/environments/node.js +++ b/lib/less/environments/node.js @@ -73,12 +73,8 @@ module.exports = { return; } - try { - data = fs.readFileSync(fullFilename, 'utf-8'); - callback(null, data, fullFilename); - } catch (e) { - callback(e); - } + data = fs.readFileSync(fullFilename, 'utf-8'); + callback(null, data, fullFilename); } else { (function tryPathIndex(i) { if (i < paths.length) { diff --git a/lib/less/parser.js b/lib/less/parser.js index 40e3592b5..0fa45b9bf 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -89,7 +89,7 @@ less.Parser = function Parser(env) { rootFilename: currentFileInfo.rootFilename }; - less.Parser.environment.loadFile(env, path, currentFileInfo.currentDirectory, function (e, contents, resolvedFilename) { + less.Parser.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { if (e) { fileParsedFunc(e); return; From f65da551683b82bae577ff6747b9a81e43b6a10f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 24 Feb 2014 09:40:18 +0000 Subject: [PATCH 015/161] Fix tests --- lib/less/browser.js | 2 +- lib/less/environments/browser.js | 13 ++- lib/less/environments/node.js | 88 ++++++++++++++++++- lib/less/parser.js | 10 ++- .../css/postProcessor/postProcessor.css | 2 +- test/browser/css/rootpath/urls.css | 16 ++-- test/browser/runner-rootpath-options.js | 2 +- 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/lib/less/browser.js b/lib/less/browser.js index 7f2517dfe..93b3f84ea 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -349,7 +349,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { relativeUrls: env.relativeUrls}; newFileInfo.entryPath = newFileInfo.currentDirectory; - newFileInfo.rootpath = less.rootpath || newFileInfo.currentDirectory; + newFileInfo.rootpath = env.rootpath || newFileInfo.currentDirectory; if (webInfo) { webInfo.remaining = remaining; diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js index efd8e9822..11cbace90 100644 --- a/lib/less/environments/browser.js +++ b/lib/less/environments/browser.js @@ -6,12 +6,12 @@ var fileCache = {}; // isFileProtocol is global function getXMLHttpRequest() { - if (window.XMLHttpRequest) { + if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject)) { return new XMLHttpRequest(); } else { try { /*global ActiveXObject */ - return new ActiveXObject("MSXML2.XMLHTTP.3.0"); + return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { log("browser doesn't support AJAX.", logLevel.errors); return null; @@ -39,6 +39,9 @@ less.Parser.environment = { isPathAbsolute: function isPathAbsolute(env, filename) { return /^(?:[a-z-]+:|\/|\\)/.test(filename); }, + alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { + return true; + }, pathDiff: function pathDiff(url, baseUrl) { // diff between two paths to create a relative path @@ -62,6 +65,12 @@ less.Parser.environment = { } return diff; }, + join: function join(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return this.extractUrlParts(laterPath, basePath).path; + }, // helper function, not part of API extractUrlParts: function extractUrlParts(url, baseUrl) { // urlParts[1] = protocol&hostname || / diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js index 2fdb36b93..7aab31744 100644 --- a/lib/less/environments/node.js +++ b/lib/less/environments/node.js @@ -19,7 +19,93 @@ module.exports = { return filename.slice(0, j + 1); }, isPathAbsolute: function(env, filename) { - return /^(?:[a-z-]+:|\/|\\)/.test(filename); + return (/^(?:[a-z-]+:|\/|\\)/).test(filename); + }, + alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { + return false; + }, + join: function join(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return basePath + laterPath; + }, + pathDiff: function pathDiff(url, baseUrl) { + // diff between two paths to create a relative path + + var urlParts = this.extractUrlParts(url), + baseUrlParts = this.extractUrlParts(baseUrl), + i, max, urlDirectories, baseUrlDirectories, diff = ""; + if (urlParts.hostPart !== baseUrlParts.hostPart) { + return ""; + } + max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); + for(i = 0; i < max; i++) { + if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } + } + baseUrlDirectories = baseUrlParts.directories.slice(i); + urlDirectories = urlParts.directories.slice(i); + for(i = 0; i < baseUrlDirectories.length-1; i++) { + diff += "../"; + } + for(i = 0; i < urlDirectories.length-1; i++) { + diff += urlDirectories[i] + "/"; + } + return diff; + }, + // helper function, not part of API + extractUrlParts: function extractUrlParts(url, baseUrl) { + // urlParts[1] = protocol&hostname || / + // urlParts[2] = / if path relative to host base + // urlParts[3] = directories + // urlParts[4] = filename + // urlParts[5] = parameters + + var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, + urlParts = url.match(urlPartsRegex), + returner = {}, directories = [], i, baseUrlParts; + + if (!urlParts) { + throw new Error("Could not parse sheet href - '"+url+"'"); + } + + // Stylesheets in IE don't always return the full path + if (baseUrl && (!urlParts[1] || urlParts[2])) { + baseUrlParts = baseUrl.match(urlPartsRegex); + if (!baseUrlParts) { + throw new Error("Could not parse page url - '"+baseUrl+"'"); + } + urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; + if (!urlParts[2]) { + urlParts[3] = baseUrlParts[3] + urlParts[3]; + } + } + + if (urlParts[3]) { + directories = urlParts[3].replace(/\\/g, "/").split("/"); + + // extract out . before .. so .. doesn't absorb a non-directory + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".") { + directories.splice(i, 1); + i -= 1; + } + } + + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".." && i > 0) { + directories.splice(i-1, 2); + i -= 2; + } + } + } + + returner.hostPart = urlParts[1]; + returner.directories = directories; + returner.path = urlParts[1] + directories.join("/"); + returner.fileUrl = returner.path + (urlParts[4] || ""); + returner.url = returner.fileUrl + (urlParts[5] || ""); + return returner; }, loadFile: function(env, filename, currentDirectory, callback) { var fullFilename, diff --git a/lib/less/parser.js b/lib/less/parser.js index b145d9cd9..f357b879a 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -103,11 +103,13 @@ less.Parser = function Parser(env) { // then rootpath should become 'less/module/nav/' // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' - var originalRelativePath = less.Parser.environment.getPath(env, path); - if(newFileInfo.relativeUrls && !less.Parser.environment.isPathAbsolute(env, path) && originalRelativePath) { - newFileInfo.rootpath = newFileInfo.rootpath + originalRelativePath; // append (sub|sup) directory path of imported file - } newFileInfo.currentDirectory = less.Parser.environment.getPath(env, resolvedFilename); + if(newFileInfo.relativeUrls) { + newFileInfo.rootpath = less.Parser.environment.join((env.rootpath || ""), less.Parser.environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!less.Parser.environment.isPathAbsolute(env, newFileInfo.rootpath) && less.Parser.environment.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = less.Parser.environment.join(newFileInfo.entryPath, newFileInfo.rootpath); + } + } newFileInfo.filename = resolvedFilename; var newEnv = new tree.parseEnv(env); diff --git a/test/browser/css/postProcessor/postProcessor.css b/test/browser/css/postProcessor/postProcessor.css index 76de30bd0..8e5489486 100644 --- a/test/browser/css/postProcessor/postProcessor.css +++ b/test/browser/css/postProcessor/postProcessor.css @@ -1,4 +1,4 @@ hr {height:50px;} .test { - color: #ffffff; + color: white; } diff --git a/test/browser/css/rootpath/urls.css b/test/browser/css/rootpath/urls.css index 9618e8e50..1a5060ef9 100644 --- a/test/browser/css/rootpath/urls.css +++ b/test/browser/css/rootpath/urls.css @@ -1,20 +1,20 @@ -@import "https://www.github.com/modify-this.css"; -@import "https://www.github.com/modify-again.css"; +@import "https://localhost/modify-this.css"; +@import "https://localhost/modify-again.css"; .modify { - my-url: url("https://www.github.com/a.png"); + my-url: url("https://localhost/a.png"); } .modify { - my-url: url("https://www.github.com/b.png"); + my-url: url("https://localhost/b.png"); } @font-face { src: url("/fonts/garamond-pro.ttf"); - src: local(Futura-Medium), url(https://www.github.com/fonts.svg#MyGeometricModern) format("svg"); + src: local(Futura-Medium), url(https://localhost/fonts.svg#MyGeometricModern) format("svg"); } #shorthands { background: url("http://www.lesscss.org/spec.html") no-repeat 0 4px; } #misc { - background-image: url(https://www.github.com/images/image.jpg); + background-image: url(https://localhost/images/image.jpg); } #data-uri { background: url(data:image/png;charset=utf-8;base64, @@ -28,8 +28,8 @@ background: transparent url('data:image/svg+xml, '); } .comma-delimited { - background: url(https://www.github.com/bg.jpg) no-repeat, url(https://www.github.com/bg.png) repeat-x top left, url(https://www.github.com/bg); + background: url(https://localhost/bg.jpg) no-repeat, url(https://localhost/bg.png) repeat-x top left, url(https://localhost/bg); } .values { - url: url('https://www.github.com/Trebuchet'); + url: url('https://localhost/Trebuchet'); } diff --git a/test/browser/runner-rootpath-options.js b/test/browser/runner-rootpath-options.js index 5e475bb40..65cb1f14c 100644 --- a/test/browser/runner-rootpath-options.js +++ b/test/browser/runner-rootpath-options.js @@ -1,3 +1,3 @@ var less = {}; -less.rootpath = "https://www.github.com/"; +less.rootpath = "https://localhost/"; From 208b32aac7a29d0f4f608201839b009000dd63c5 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 24 Feb 2014 09:41:31 +0000 Subject: [PATCH 016/161] remove commented out code now implemented in parser --- lib/less/environments/browser.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js index 11cbace90..ae45d1199 100644 --- a/lib/less/environments/browser.js +++ b/lib/less/environments/browser.js @@ -172,15 +172,6 @@ less.Parser.environment = { var hrefParts = this.extractUrlParts(filename, window.location.href); var href = hrefParts.url; - // TODO - use pathDiff in less.node - /*if (env.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - }*/ - if (env.useFileCache && fileCache[href]) { try { var lessText = fileCache[href]; From 08bd23dc2ae76b4a36504d5b069d8ef7b6467287 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 24 Feb 2014 21:22:52 +0000 Subject: [PATCH 017/161] add browserify (not yet working) and refactor tree nodes to not be dependent on their parent (currently breaks browser build) --- Gruntfile.js | 8 + build/build.yml | 2 +- lib/less/colors.js | 4 +- lib/less/encoder.js | 4 - lib/less/env.js | 4 +- lib/less/environments/node.js | 3 + lib/less/extend-visitor.js | 4 +- lib/less/functions.js | 25 ++- lib/less/import-visitor.js | 4 +- lib/less/index.js | 192 +++++++---------- lib/less/join-selector-visitor.js | 4 +- lib/less/non-node-index.js | 18 ++ lib/less/parser.js | 18 +- lib/less/source-map-output.js | 4 +- lib/less/to-css-visitor.js | 5 +- lib/less/tree.js | 45 +++- lib/less/tree/alpha.js | 12 +- lib/less/tree/anonymous.js | 12 +- lib/less/tree/assignment.js | 12 +- lib/less/tree/attribute.js | 29 +++ lib/less/tree/call.js | 12 +- lib/less/tree/color.js | 18 +- lib/less/tree/combinator.js | 40 ++++ lib/less/tree/comment.js | 10 +- lib/less/tree/condition.js | 10 +- lib/less/tree/detached-ruleset.js | 11 +- lib/less/tree/dimension.js | 177 +--------------- lib/less/tree/directive.js | 12 +- lib/less/tree/element.js | 73 +------ lib/less/tree/expression.js | 12 +- lib/less/tree/extend.js | 18 +- lib/less/tree/import.js | 14 +- lib/less/tree/javascript.js | 11 +- lib/less/tree/keyword.js | 17 +- lib/less/tree/media.js | 12 +- lib/less/tree/mixin-call.js | 152 ++++++++++++++ lib/less/tree/mixin-definition.js | 165 +++++++++++++++ lib/less/tree/mixin.js | 315 ---------------------------- lib/less/tree/negative.js | 12 +- lib/less/tree/operation.js | 13 +- lib/less/tree/paren.js | 13 +- lib/less/tree/quoted.js | 10 +- lib/less/tree/rule.js | 14 +- lib/less/tree/ruleset-call.js | 10 +- lib/less/tree/ruleset.js | 19 +- lib/less/tree/selector.js | 12 +- lib/less/tree/unicode-descriptor.js | 10 +- lib/less/tree/unit-conversions.js | 21 ++ lib/less/tree/unit.js | 137 ++++++++++++ lib/less/tree/url.js | 12 +- lib/less/tree/value.js | 12 +- lib/less/tree/variable.js | 12 +- lib/less/visitor.js | 4 +- package.json | 3 +- 54 files changed, 921 insertions(+), 881 deletions(-) delete mode 100644 lib/less/encoder.js create mode 100644 lib/less/non-node-index.js create mode 100644 lib/less/tree/attribute.js create mode 100644 lib/less/tree/combinator.js create mode 100644 lib/less/tree/mixin-call.js create mode 100644 lib/less/tree/mixin-definition.js delete mode 100644 lib/less/tree/mixin.js create mode 100644 lib/less/tree/unit-conversions.js create mode 100644 lib/less/tree/unit.js diff --git a/Gruntfile.js b/Gruntfile.js index 3fba33ea2..b47a0dc4b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -47,6 +47,14 @@ module.exports = function(grunt) { 'node node_modules/http-server/bin/http-server test/sourcemaps -p 8084'].join('&&') } }, + + browserify: { + dist: { + files: { + 'dist/less.js': ['lib/less/non-node-index.js', 'lib/less/browser.js', 'lib/less/environment/browser.js'] + } + } + }, concat: { options: { stripBanners: 'all', diff --git a/build/build.yml b/build/build.yml index 2dd328e1c..098901e44 100644 --- a/build/build.yml +++ b/build/build.yml @@ -137,7 +137,7 @@ tree: - <%= build.lib %>/tree/javascript.js - <%= build.lib %>/tree/keyword.js - <%= build.lib %>/tree/media.js - - <%= build.lib %>/tree/mixin.js + - <%= build.lib %>/tree/mixin-call.js - <%= build.lib %>/tree/negative.js - <%= build.lib %>/tree/operation.js - <%= build.lib %>/tree/paren.js diff --git a/lib/less/colors.js b/lib/less/colors.js index e509b6025..dd213f2cd 100644 --- a/lib/less/colors.js +++ b/lib/less/colors.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.colors = { 'aliceblue':'#f0f8ff', 'antiquewhite':'#faebd7', @@ -148,4 +148,4 @@ 'yellow':'#ffff00', 'yellowgreen':'#9acd32' }; -})(require('./tree')); +}; diff --git a/lib/less/encoder.js b/lib/less/encoder.js deleted file mode 100644 index 65902f7a5..000000000 --- a/lib/less/encoder.js +++ /dev/null @@ -1,4 +0,0 @@ -// base64 encoder implementation for node -exports.encodeBase64 = function(str) { - return new Buffer(str).toString('base64'); -}; diff --git a/lib/less/env.js b/lib/less/env.js index 41eabe507..801929410 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { var parseCopyProperties = [ 'paths', // option - unmodified - paths to search for imports on @@ -132,4 +132,4 @@ } }; -})(require('./tree')); +}; diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js index 7aab31744..a4cae9be2 100644 --- a/lib/less/environments/node.js +++ b/lib/less/environments/node.js @@ -8,6 +8,9 @@ module.exports = { warn: function(env, msg) { console.warn(msg); }, + encodeBase64: function encodeBase64(env, str) { + return new Buffer(str).toString('base64'); + }, getPath: function (env, filename) { var j = filename.lastIndexOf('/'); if (j < 0) { diff --git a/lib/less/extend-visitor.js b/lib/less/extend-visitor.js index 4b397a985..211e6adde 100644 --- a/lib/less/extend-visitor.js +++ b/lib/less/extend-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { /*jshint loopfunc:true */ tree.extendFinderVisitor = function() { @@ -413,4 +413,4 @@ } }; -})(require('./tree')); +}; diff --git a/lib/less/functions.js b/lib/less/functions.js index 5d4c0ebf3..8fac47aa1 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (less, tree) { -tree.functions = { +var functions = { rgb: function (r, g, b) { return this.rgba(r, g, b, 1.0); }, @@ -530,7 +530,7 @@ tree.functions = { if (useBase64) { try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation + returner = less.Parser.environment.encodeBase64(this.env, returner); } catch(e) { useBase64 = false; } @@ -687,30 +687,30 @@ tree.defaultFunc = { }; function initFunctions() { - var f, tf = tree.functions; + var f; // math for (f in mathFunctions) { if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); + functions[f] = _math.bind(null, Math[f], mathFunctions[f]); } } // color blending for (f in colorBlendMode) { if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); + functions[f] = colorBlend.bind(null, colorBlendMode[f]); } } // default f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); + functions["default"] = f.eval.bind(f); } initFunctions(); function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); + return functions.hsla(color.h, color.s, color.l, color.a); } function scaled(n, size) { @@ -748,11 +748,14 @@ tree.fround = function(env, value) { } }; -tree.functionCall = function(env, currentFileInfo) { +tree.functionCall = function(env, currentFileInfo, environment) { this.env = env; + this.environment = environment; this.currentFileInfo = currentFileInfo; }; -tree.functionCall.prototype = tree.functions; +tree.functionCall.prototype = functions; -})(require('./tree')); +return functions; + +}; diff --git a/lib/less/import-visitor.js b/lib/less/import-visitor.js index 0d773ca05..7b1cbccf3 100644 --- a/lib/less/import-visitor.js +++ b/lib/less/import-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.importVisitor = function(importer, finish, evalEnv) { this._visitor = new tree.visitor(this); this._importer = importer; @@ -115,4 +115,4 @@ } }; -})(require('./tree')); \ No newline at end of file +}; diff --git a/lib/less/index.js b/lib/less/index.js index 0138cd3de..a2c69175c 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,137 +1,95 @@ -var less = { - version: [1, 6, 3], - Parser: require('./parser').Parser, - tree: require('./tree'), - render: function (input, options, callback) { - options = options || {}; - - if (typeof(options) === 'function') { - callback = options; - options = {}; - } +var less = require("./non-node-index.js"); + +less.render = function (input, options, callback) { + options = options || {}; - var parser = new(less.Parser)(options), - ee; + if (typeof(options) === 'function') { + callback = options; + options = {}; + } - if (callback) { + var parser = new(less.Parser)(options), + ee; + + if (callback) { + parser.parse(input, function (e, root) { + if (e) { callback(e); return; } + var css; + try { + css = root && root.toCSS && root.toCSS(options); + } + catch (err) { callback(err); return; } + callback(null, css); + }); + } else { + ee = new (require('events').EventEmitter)(); + + process.nextTick(function () { parser.parse(input, function (e, root) { - if (e) { callback(e); return; } - var css; - try { - css = root && root.toCSS && root.toCSS(options); - } - catch (err) { callback(err); return; } - callback(null, css); + if (e) { return ee.emit('error', e); } + try { ee.emit('success', root.toCSS(options)); } + catch (err) { ee.emit('error', err); } }); - } else { - ee = new (require('events').EventEmitter)(); - - process.nextTick(function () { - parser.parse(input, function (e, root) { - if (e) { return ee.emit('error', e); } - try { ee.emit('success', root.toCSS(options)); } - catch (err) { ee.emit('error', err); } - }); - }); - return ee; - } - }, - formatError: function(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; + }); + return ee; + } +}; +less.formatError = function(ctx, options) { + options = options || {}; - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } + var message = ""; + var extract = ctx.extract; + var error = []; + var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } + // only output a stack if it isn't a less error + if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } + if (!ctx.hasOwnProperty('index') || !extract) { + return ctx.stack || ctx.message; + } - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } + if (typeof(extract[0]) === 'string') { + error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); + } - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); + if (typeof(extract[1]) === 'string') { + var errorTxt = ctx.line + ' '; + if (extract[1]) { + errorTxt += extract[1].slice(0, ctx.column) + + stylize(stylize(stylize(extract[1][ctx.column], 'bold') + + extract[1].slice(ctx.column + 1), 'red'), 'inverse'); } - error = error.join('\n') + stylize('', 'reset') + '\n'; + error.push(errorTxt); + } - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } + if (typeof(extract[2]) === 'string') { + error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); + } + error = error.join('\n') + stylize('', 'reset') + '\n'; - message += '\n' + error; + message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); + if (ctx.filename) { + message += stylize(' in ', 'red') + ctx.filename + + stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); + } - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } + message += '\n' + error; - return message; - }, - writeError: function (ctx, options) { - options = options || {}; - if (options.silent) { return; } - console.error(less.formatError(ctx, options)); + if (ctx.callLine) { + message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; + message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; } + + return message; +}; + +less.writeError = function (ctx, options) { + options = options || {}; + if (options.silent) { return; } + console.error(less.formatError(ctx, options)); }; less.Parser.environment = require("./environments/node"); -require('./tree/color'); -require('./tree/directive'); -require('./tree/detached-ruleset'); -require('./tree/operation'); -require('./tree/dimension'); -require('./tree/keyword'); -require('./tree/variable'); -require('./tree/ruleset'); -require('./tree/element'); -require('./tree/selector'); -require('./tree/quoted'); -require('./tree/expression'); -require('./tree/rule'); -require('./tree/call'); -require('./tree/url'); -require('./tree/alpha'); -require('./tree/import'); -require('./tree/mixin'); -require('./tree/comment'); -require('./tree/anonymous'); -require('./tree/value'); -require('./tree/javascript'); -require('./tree/assignment'); -require('./tree/condition'); -require('./tree/paren'); -require('./tree/media'); -require('./tree/unicode-descriptor'); -require('./tree/negative'); -require('./tree/extend'); -require('./tree/ruleset-call'); -require('./env'); -require('./functions'); -require('./colors'); -require('./visitor.js'); -require('./import-visitor.js'); -require('./extend-visitor.js'); -require('./join-selector-visitor.js'); -require('./to-css-visitor.js'); -require('./source-map-output.js'); - module.exports = less; diff --git a/lib/less/join-selector-visitor.js b/lib/less/join-selector-visitor.js index 75a61d6ec..13b3a590e 100644 --- a/lib/less/join-selector-visitor.js +++ b/lib/less/join-selector-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.joinSelectorVisitor = function() { this.contexts = [[]]; this._visitor = new tree.visitor(this); @@ -41,4 +41,4 @@ } }; -})(require('./tree')); \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js new file mode 100644 index 000000000..5c3f8c7ec --- /dev/null +++ b/lib/less/non-node-index.js @@ -0,0 +1,18 @@ +var less = { + version: [1, 6, 3] +}; + +less.tree = (require('./tree'))(less); +less.Parser = (require('./parser'))(less, less.tree); +less.tree.functions = (require('./functions'))(less, less.tree); +require('./env')(less.tree); + +require('./colors')(less.tree); +require('./visitor.js')(less.tree); +require('./import-visitor.js')(less.tree); +require('./extend-visitor.js')(less.tree); +require('./join-selector-visitor.js')(less.tree); +require('./to-css-visitor.js')(less.tree); +require('./source-map-output.js')(less.tree); + +module.exports = less; diff --git a/lib/less/parser.js b/lib/less/parser.js index f357b879a..8c94f8bdd 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,11 +1,4 @@ -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} +module.exports = function(less, tree) { // // less.js - parser // @@ -39,7 +32,7 @@ if (less === undefined) { // It also takes care of moving all the indices forwards. // // -less.Parser = function Parser(env) { +var Parser = function Parser(env) { var input, // LeSS input string i, // current index in `input` j, // current chunk @@ -2064,16 +2057,19 @@ less.Parser = function Parser(env) { }; return parser; }; -less.Parser.serializeVars = function(vars) { +Parser.serializeVars = function(vars) { var s = ''; for (var name in vars) { if (Object.hasOwnProperty.call(vars, name)) { var value = vars[name]; s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); + ((('' + value).slice(-1) === ';') ? '' : ';'); } } return s; }; + +return Parser; +}; \ No newline at end of file diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 9a090d4e4..2fd10ec2b 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.sourceMapOutput = function (options) { this._css = []; @@ -138,4 +138,4 @@ return this._css.join(''); }; -})(require('./tree')); +}; diff --git a/lib/less/to-css-visitor.js b/lib/less/to-css-visitor.js index 53f4ee03a..c58f11a60 100644 --- a/lib/less/to-css-visitor.js +++ b/lib/less/to-css-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.toCSSVisitor = function(env) { this._visitor = new tree.visitor(this); this._env = env; @@ -236,5 +236,4 @@ }); } }; - -})(require('./tree')); \ No newline at end of file +}; diff --git a/lib/less/tree.js b/lib/less/tree.js index cd1352f59..fac177b48 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -1,5 +1,7 @@ -(function (tree) { +module.exports = function (less) { +var tree = {}; + tree.debugInfo = function(env, ctx, lineSeperator) { var result=""; if (env.dumpLineNumbers && !env.compress) { @@ -94,4 +96,43 @@ tree.outputRuleset = function (env, output, rules) { env.tabLevel--; }; -})(require('./tree')); +tree.Alpha = require('./tree/alpha')(tree); +tree.Color = require('./tree/color')(tree); +tree.Directive = require('./tree/directive')(tree); +tree.DetachedRuleset = require('./tree/detached-ruleset')(tree); +tree.Operation = require('./tree/operation')(tree); +tree.Dimension = require('./tree/dimension')(tree, require('./tree/unit-conversions')); //todo move conversions +tree.Unit = require('./tree/unit')(tree, require('./tree/unit-conversions')); +tree.Keyword = require('./tree/keyword')(tree); +tree.Variable = require('./tree/variable')(tree); +tree.Ruleset = require('./tree/ruleset')(tree); +tree.Element = require('./tree/element')(tree); +tree.Attribute = require('./tree/attribute')(tree); +tree.Combinator = require('./tree/combinator')(tree); +tree.Selector = require('./tree/selector')(tree); +tree.Quoted = require('./tree/quoted')(tree); +tree.Expression = require('./tree/expression')(tree); +tree.Rule = require('./tree/rule')(tree); +tree.Call = require('./tree/call')(tree); +tree.URL = require('./tree/url')(tree); +tree.Import = require('./tree/import')(tree); +tree.mixin = { + Call: require('./tree/mixin-call')(tree), + Definition: require('./tree/mixin-definition')(tree) +}; +tree.Comment = require('./tree/comment')(tree); +tree.Anonymous = require('./tree/anonymous')(tree); +tree.Value = require('./tree/value')(tree); +tree.JavaScript = require('./tree/javascript')(tree); +tree.Assignment = require('./tree/assignment')(tree); +tree.Condition = require('./tree/condition')(tree); +tree.Paren = require('./tree/paren')(tree); +tree.Media = require('./tree/media')(tree); +tree.UnicodeDescriptor = require('./tree/unicode-descriptor')(tree); +tree.Negative = require('./tree/negative')(tree); +tree.Extend = require('./tree/extend')(tree); +tree.RulesetCall = require('./tree/ruleset-call')(tree); + +return tree; + +}; \ No newline at end of file diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js index e827fd08a..eb247adf5 100644 --- a/lib/less/tree/alpha.js +++ b/lib/less/tree/alpha.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree) { -tree.Alpha = function (val) { +var Alpha = function (val) { this.value = val; }; -tree.Alpha.prototype = { +Alpha.prototype = { type: "Alpha", accept: function (visitor) { this.value = visitor.visit(this.value); }, eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } + if (this.value.eval) { return new Alpha(this.value.eval(env)); } return this; }, genCSS: function (env, output) { @@ -25,5 +25,5 @@ tree.Alpha.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Alpha; +}; diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 3cfae7712..e6be3f089 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree) { -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { +var Anonymous = function (string, index, currentFileInfo, mapLines) { this.value = string.value || string; this.index = index; this.mapLines = mapLines; this.currentFileInfo = currentFileInfo; }; -tree.Anonymous.prototype = { +Anonymous.prototype = { type: "Anonymous", eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); + return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); }, compare: function (x) { if (!x.toCSS) { @@ -30,5 +30,5 @@ tree.Anonymous.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Anonymous; +}; diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index 61d22fca7..afe62933b 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -1,17 +1,17 @@ -(function (tree) { +module.exports = function (tree) { -tree.Assignment = function (key, val) { +var Assignment = function (key, val) { this.key = key; this.value = val; }; -tree.Assignment.prototype = { +Assignment.prototype = { type: "Assignment", accept: function (visitor) { this.value = visitor.visit(this.value); }, eval: function (env) { if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); + return new(Assignment)(this.key, this.value.eval(env)); } return this; }, @@ -25,5 +25,5 @@ tree.Assignment.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Assignment; +}; diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js new file mode 100644 index 000000000..4103bfc4a --- /dev/null +++ b/lib/less/tree/attribute.js @@ -0,0 +1,29 @@ +module.exports = function (tree) { + +var Attribute = function (key, op, value) { + this.key = key; + this.op = op; + this.value = value; +}; +Attribute.prototype = { + type: "Attribute", + eval: function (env) { + return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key, + this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); + }, + genCSS: function (env, output) { + output.add(this.toCSS(env)); + }, + toCSS: function (env) { + var value = this.key.toCSS ? this.key.toCSS(env) : this.key; + + if (this.op) { + value += this.op; + value += (this.value.toCSS ? this.value.toCSS(env) : this.value); + } + + return '[' + value + ']'; + } +}; +return Attribute; +}; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index aa6acb9c8..e4a51013c 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree) { // // A function call node. // -tree.Call = function (name, args, index, currentFileInfo) { +var Call = function (name, args, index, currentFileInfo) { this.name = name; this.args = args; this.index = index; this.currentFileInfo = currentFileInfo; }; -tree.Call.prototype = { +Call.prototype = { type: "Call", accept: function (visitor) { if (this.args) { @@ -49,7 +49,7 @@ tree.Call.prototype = { } } - return new tree.Call(this.name, args, this.index, this.currentFileInfo); + return new Call(this.name, args, this.index, this.currentFileInfo); }, genCSS: function (env, output) { @@ -67,5 +67,5 @@ tree.Call.prototype = { toCSS: tree.toCSS }; - -})(require('../tree')); +return Call; +}; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 3d841b6b6..c902a1393 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -1,8 +1,8 @@ -(function (tree) { +module.exports = function (tree) { // // RGB Colors - #ff0014, #eee // -tree.Color = function (rgb, a) { +var Color = function (rgb, a) { // // The end goal here, is to parse the arguments // into an integer triplet, such as `128, 255, 0` @@ -23,7 +23,7 @@ tree.Color = function (rgb, a) { this.alpha = typeof(a) === 'number' ? a : 1; }; -tree.Color.prototype = { +Color.prototype = { type: "Color", eval: function () { return this; }, luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, @@ -79,7 +79,7 @@ tree.Color.prototype = { for (var c = 0; c < 3; c++) { rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); } - return new(tree.Color)(rgb, alpha); + return new(Color)(rgb, alpha); }, toRGB: function () { @@ -153,13 +153,13 @@ tree.Color.prototype = { } }; -tree.Color.fromKeyword = function(keyword) { +Color.fromKeyword = function(keyword) { var c, key = keyword.toLowerCase(); if (tree.colors.hasOwnProperty(key)) { - c = new(tree.Color)(tree.colors[key].slice(1)); + c = new(Color)(tree.colors[key].slice(1)); } else if (key === "transparent") { - c = new(tree.Color)([0, 0, 0], 0); + c = new(Color)([0, 0, 0], 0); } if (c) { @@ -178,5 +178,5 @@ function toHex(v) { function clamp(v, max) { return Math.min(Math.max(v, 0), max); } - -})(require('../tree')); +return Color; +}; diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js new file mode 100644 index 000000000..0fd4dd87a --- /dev/null +++ b/lib/less/tree/combinator.js @@ -0,0 +1,40 @@ +module.exports = function (tree) { + +var Combinator = function (value) { + if (value === ' ') { + this.value = ' '; + } else { + this.value = value ? value.trim() : ""; + } +}; +Combinator.prototype = { + type: "Combinator", + _outputMap: { + '' : '', + ' ' : ' ', + ':' : ' :', + '+' : ' + ', + '~' : ' ~ ', + '>' : ' > ', + '|' : '|', + '^' : ' ^ ', + '^^' : ' ^^ ' + }, + _outputMapCompressed: { + '' : '', + ' ' : ' ', + ':' : ' :', + '+' : '+', + '~' : '~', + '>' : '>', + '|' : '|', + '^' : '^', + '^^' : '^^' + }, + genCSS: function (env, output) { + output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); + }, + toCSS: tree.toCSS +}; +return Combinator; +}; diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index c39606e09..05bc2207a 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.Comment = function (value, silent, index, currentFileInfo) { +var Comment = function (value, silent, index, currentFileInfo) { this.value = value; this.silent = !!silent; this.currentFileInfo = currentFileInfo; }; -tree.Comment.prototype = { +Comment.prototype = { type: "Comment", genCSS: function (env, output) { if (this.debugInfo) { @@ -24,5 +24,5 @@ tree.Comment.prototype = { this.isReferenced = true; } }; - -})(require('../tree')); +return Comment; +}; diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 56d221efb..fdc798121 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -1,13 +1,13 @@ -(function (tree) { +module.exports = function (tree) { -tree.Condition = function (op, l, r, i, negate) { +var Condition = function (op, l, r, i, negate) { this.op = op.trim(); this.lvalue = l; this.rvalue = r; this.index = i; this.negate = negate; }; -tree.Condition.prototype = { +Condition.prototype = { type: "Condition", accept: function (visitor) { this.lvalue = visitor.visit(this.lvalue); @@ -45,5 +45,5 @@ tree.Condition.prototype = { return this.negate ? !result : result; } }; - -})(require('../tree')); +return Condition; +}; diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js index 0a5512aae..a58029cad 100644 --- a/lib/less/tree/detached-ruleset.js +++ b/lib/less/tree/detached-ruleset.js @@ -1,20 +1,21 @@ -(function (tree) { +module.exports = function (tree) { -tree.DetachedRuleset = function (ruleset, frames) { +var DetachedRuleset = function (ruleset, frames) { this.ruleset = ruleset; this.frames = frames; }; -tree.DetachedRuleset.prototype = { +DetachedRuleset.prototype = { type: "DetachedRuleset", accept: function (visitor) { this.ruleset = visitor.visit(this.ruleset); }, eval: function (env) { var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); + return new DetachedRuleset(this.ruleset, frames); }, callEval: function (env) { return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); } }; -})(require('../tree')); +return DetachedRuleset; +}; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index aecec136b..b74becc4d 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree, unitConversions) { // // A number with a unit // -tree.Dimension = function (value, unit) { +var Dimension = function (value, unit) { this.value = parseFloat(value); this.unit = (unit && unit instanceof tree.Unit) ? unit : new(tree.Unit)(unit ? [unit] : undefined); }; -tree.Dimension.prototype = { +Dimension.prototype = { type: "Dimension", accept: function (visitor) { this.unit = visitor.visit(this.unit); @@ -84,11 +84,11 @@ tree.Dimension.prototype = { unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); unit.cancel(); } - return new(tree.Dimension)(value, unit); + return new(Dimension)(value, unit); }, compare: function (other) { - if (other instanceof tree.Dimension) { + if (other instanceof Dimension) { var a, b, aValue, bValue; @@ -126,8 +126,8 @@ tree.Dimension.prototype = { i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { + for(i in unitConversions) { + if (unitConversions[i].hasOwnProperty(conversions)) { derivedConversions = {}; derivedConversions[i] = conversions; } @@ -152,7 +152,7 @@ tree.Dimension.prototype = { for (groupName in conversions) { if (conversions.hasOwnProperty(groupName)) { targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; + group = unitConversions[groupName]; unit.map(applyUnit); } @@ -160,165 +160,8 @@ tree.Dimension.prototype = { unit.cancel(); - return new(tree.Dimension)(value, unit); + return new(Dimension)(value, unit); } }; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } +return Dimension; }; - -})(require('../tree')); diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index b59a16bd2..1bb9b6cf2 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { +var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { this.name = name; this.value = value; if (rules) { @@ -12,7 +12,7 @@ tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo this.debugInfo = debugInfo; }; -tree.Directive.prototype = { +Directive.prototype = { type: "Directive", accept: function (visitor) { var value = this.value, rules = this.rules; @@ -46,7 +46,7 @@ tree.Directive.prototype = { rules = rules.eval(env); rules.root = true; } - return new(tree.Directive)(this.name, value, rules, + return new(Directive)(this.name, value, rules, this.index, this.currentFileInfo, this.debugInfo); }, variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, @@ -65,5 +65,5 @@ tree.Directive.prototype = { } } }; - -})(require('../tree')); +return Directive; +}; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 3869f6c15..925b4b936 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Element = function (combinator, value, index, currentFileInfo) { +var Element = function (combinator, value, index, currentFileInfo) { this.combinator = combinator instanceof tree.Combinator ? combinator : new(tree.Combinator)(combinator); @@ -14,7 +14,7 @@ tree.Element = function (combinator, value, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; }; -tree.Element.prototype = { +Element.prototype = { type: "Element", accept: function (visitor) { var value = this.value; @@ -24,7 +24,7 @@ tree.Element.prototype = { } }, eval: function (env) { - return new(tree.Element)(this.combinator, + return new(Element)(this.combinator, this.value.eval ? this.value.eval(env) : this.value, this.index, this.currentFileInfo); @@ -41,68 +41,5 @@ tree.Element.prototype = { } } }; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } +return Element; }; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index a3d1e4a65..4614e1559 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -1,7 +1,7 @@ -(function (tree) { +module.exports = function (tree) { -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { +var Expression = function (value) { this.value = value; }; +Expression.prototype = { type: "Expression", accept: function (visitor) { if (this.value) { @@ -16,7 +16,7 @@ tree.Expression.prototype = { env.inParenthesis(); } if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { + returnValue = new(Expression)(this.value.map(function (e) { return e.eval(env); })); } else if (this.value.length === 1) { @@ -50,5 +50,5 @@ tree.Expression.prototype = { }); } }; - -})(require('../tree')); +return Expression; +}; diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js index ef9d88e40..746bdeb38 100644 --- a/lib/less/tree/extend.js +++ b/lib/less/tree/extend.js @@ -1,10 +1,10 @@ -(function (tree) { +module.exports = function (tree) { -tree.Extend = function Extend(selector, option, index) { +var Extend = function Extend(selector, option, index) { this.selector = selector; this.option = option; this.index = index; - this.object_id = tree.Extend.next_id++; + this.object_id = Extend.next_id++; this.parent_ids = [this.object_id]; switch(option) { @@ -18,18 +18,18 @@ tree.Extend = function Extend(selector, option, index) { break; } }; -tree.Extend.next_id = 0; +Extend.next_id = 0; -tree.Extend.prototype = { +Extend.prototype = { type: "Extend", accept: function (visitor) { this.selector = visitor.visit(this.selector); }, eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); + return new(Extend)(this.selector.eval(env), this.option, this.index); }, clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); + return new(Extend)(this.selector, this.option, this.index); }, findSelfSelectors: function (selectors) { var selfElements = [], @@ -49,5 +49,5 @@ tree.Extend.prototype = { this.selfSelectors = [{ elements: selfElements }]; } }; - -})(require('../tree')); +return Extend; +}; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 910fc11a7..4f586ad12 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { // // CSS @import node // @@ -11,7 +11,7 @@ // `import,push`, we also pass it a callback, which it'll call once // the file has been fetched, and parsed. // -tree.Import = function (path, features, options, index, currentFileInfo) { +var Import = function (path, features, options, index, currentFileInfo) { this.options = options; this.index = index; this.path = path; @@ -37,7 +37,7 @@ tree.Import = function (path, features, options, index, currentFileInfo) { // we end up with a flat structure, which can easily be imported in the parent // ruleset. // -tree.Import.prototype = { +Import.prototype = { type: "Import", accept: function (visitor) { if (this.features) { @@ -70,7 +70,7 @@ tree.Import.prototype = { return null; }, evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); + return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); }, evalPath: function (env) { var path = this.path.eval(env); @@ -99,7 +99,7 @@ tree.Import.prototype = { var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); + var newImport = new(Import)(this.evalPath(env), features, this.options, this.index); if (!newImport.css && this.error) { throw this.error; } @@ -113,5 +113,5 @@ tree.Import.prototype = { } } }; - -})(require('../tree')); +return Import; +}; diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index 30a8559d6..fdca7bf8a 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.JavaScript = function (string, index, escaped) { +var JavaScript = function (string, index, escaped) { this.escaped = escaped; this.expression = string; this.index = index; }; -tree.JavaScript.prototype = { +JavaScript.prototype = { type: "JavaScript", eval: function (env) { var result, @@ -53,6 +53,5 @@ tree.JavaScript.prototype = { } } }; - -})(require('../tree')); - +return JavaScript; +}; diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 830594d88..46f564841 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -1,7 +1,7 @@ -(function (tree) { +module.exports = function (tree) { -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { +var Keyword = function (value) { this.value = value; }; +Keyword.prototype = { type: "Keyword", eval: function () { return this; }, genCSS: function (env, output) { @@ -10,7 +10,7 @@ tree.Keyword.prototype = { }, toCSS: tree.toCSS, compare: function (other) { - if (other instanceof tree.Keyword) { + if (other instanceof Keyword) { return other.value === this.value ? 0 : 1; } else { return -1; @@ -18,7 +18,10 @@ tree.Keyword.prototype = { } }; -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); +//TODO move? +tree.True = new(Keyword)('true'); +tree.False = new(Keyword)('false'); -})(require('../tree')); +return Keyword; + +}; diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 095e43e88..34c07bb7b 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Media = function (value, features, index, currentFileInfo) { +var Media = function (value, features, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; @@ -10,7 +10,7 @@ tree.Media = function (value, features, index, currentFileInfo) { this.rules = [new(tree.Ruleset)(selectors, value)]; this.rules[0].allowImports = true; }; -tree.Media.prototype = { +Media.prototype = { type: "Media", accept: function (visitor) { if (this.features) { @@ -32,7 +32,7 @@ tree.Media.prototype = { env.mediaPath = []; } - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); + var media = new(Media)(null, [], this.index, this.currentFileInfo); if(this.debugInfo) { this.rules[0].debugInfo = this.debugInfo; media.debugInfo = this.debugInfo; @@ -153,5 +153,5 @@ tree.Media.prototype = { this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; } }; - -})(require('../tree')); +return Media; +}; diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js new file mode 100644 index 000000000..606600aeb --- /dev/null +++ b/lib/less/tree/mixin-call.js @@ -0,0 +1,152 @@ +module.exports = function (tree) { + +var Call = function (elements, args, index, currentFileInfo, important) { + this.selector = new(tree.Selector)(elements); + this.arguments = (args && args.length) ? args : null; + this.index = index; + this.currentFileInfo = currentFileInfo; + this.important = important; +}; +Call.prototype = { + type: "MixinCall", + accept: function (visitor) { + if (this.selector) { + this.selector = visitor.visit(this.selector); + } + if (this.arguments) { + this.arguments = visitor.visitArray(this.arguments); + } + }, + eval: function (env) { + var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, + candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, + defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; + + args = this.arguments && this.arguments.map(function (a) { + return { name: a.name, value: a.value.eval(env) }; + }); + + for (i = 0; i < env.frames.length; i++) { + if ((mixins = env.frames[i].find(this.selector)).length > 0) { + isOneFound = true; + + // 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. + + for (m = 0; m < mixins.length; m++) { + mixin = mixins[m]; + isRecursive = false; + for(f = 0; f < env.frames.length; f++) { + if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { + isRecursive = true; + break; + } + } + if (isRecursive) { + continue; + } + + if (mixin.matchArgs(args, env)) { + candidate = {mixin: mixin, group: defNone}; + + if (mixin.matchCondition) { + for (f = 0; f < 2; f++) { + 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.push(candidate); + } + } + else { + candidates.push(candidate); + } + + match = true; + } + } + + defaultFunc.reset(); + + count = [0, 0, 0]; + for (m = 0; m < candidates.length; m++) { + count[candidates[m].group]++; + } + + if (count[defNone] > 0) { + defaultResult = defFalse; + } else { + defaultResult = defTrue; + if ((count[defTrue] + count[defFalse]) > 1) { + throw { type: 'Runtime', + message: 'Ambiguous use of `default()` found when matching for `' + + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } + } + + for (m = 0; m < candidates.length; m++) { + candidate = candidates[m].group; + if ((candidate === defNone) || (candidate === defaultResult)) { + try { + mixin = candidates[m].mixin; + if (!(mixin instanceof tree.mixin.Definition)) { + mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); + mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; + } + Array.prototype.push.apply( + rules, mixin.evalCall(env, args, this.important).rules); + } catch (e) { + throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; + } + } + } + + if (match) { + if (!this.currentFileInfo || !this.currentFileInfo.reference) { + for (i = 0; i < rules.length; i++) { + rule = rules[i]; + if (rule.markReferenced) { + rule.markReferenced(); + } + } + } + return rules; + } + } + } + if (isOneFound) { + throw { type: 'Runtime', + message: 'No matching definition was found for `' + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } else { + throw { type: 'Name', + message: this.selector.toCSS().trim() + " is undefined", + index: this.index, filename: this.currentFileInfo.filename }; + } + }, + format: function (args) { + return this.selector.toCSS().trim() + '(' + + (args ? args.map(function (a) { + var argValue = ""; + if (a.name) { + argValue += a.name + ":"; + } + if (a.value.toCSS) { + argValue += a.value.toCSS(); + } else { + argValue += "???"; + } + return argValue; + }).join(', ') : "") + ")"; + } +}; +return Call; +}; diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js new file mode 100644 index 000000000..b661c3101 --- /dev/null +++ b/lib/less/tree/mixin-definition.js @@ -0,0 +1,165 @@ +module.exports = function (tree) { + +var Definition = function (name, params, rules, condition, variadic, frames) { + this.name = name; + this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; + this.params = params; + this.condition = condition; + this.variadic = variadic; + this.arity = params.length; + this.rules = rules; + this._lookups = {}; + this.required = params.reduce(function (count, p) { + if (!p.name || (p.name && !p.value)) { return count + 1; } + else { return count; } + }, 0); + this.parent = tree.Ruleset.prototype; + this.frames = frames; +}; +Definition.prototype = { + type: "MixinDefinition", + accept: function (visitor) { + if (this.params && this.params.length) { + this.params = visitor.visitArray(this.params); + } + this.rules = visitor.visitArray(this.rules); + if (this.condition) { + this.condition = visitor.visit(this.condition); + } + }, + variable: function (name) { return this.parent.variable.call(this, name); }, + variables: function () { return this.parent.variables.call(this); }, + find: function () { return this.parent.find.apply(this, arguments); }, + rulesets: function () { return this.parent.rulesets.apply(this); }, + + evalParams: function (env, mixinEnv, args, evaldArguments) { + /*jshint boss:true */ + var frame = new(tree.Ruleset)(null, null), + varargs, arg, + params = this.params.slice(0), + i, j, val, name, isNamedFound, argIndex, argsLength = 0; + + mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); + + if (args) { + args = args.slice(0); + argsLength = args.length; + + for(i = 0; i < argsLength; i++) { + arg = args[i]; + if (name = (arg && arg.name)) { + isNamedFound = false; + for(j = 0; j < params.length; j++) { + if (!evaldArguments[j] && name === params[j].name) { + evaldArguments[j] = arg.value.eval(env); + frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); + isNamedFound = true; + break; + } + } + if (isNamedFound) { + args.splice(i, 1); + i--; + continue; + } else { + throw { type: 'Runtime', message: "Named argument for " + this.name + + ' ' + args[i].name + ' not found' }; + } + } + } + } + argIndex = 0; + for (i = 0; i < params.length; i++) { + if (evaldArguments[i]) { continue; } + + arg = args && args[argIndex]; + + if (name = params[i].name) { + if (params[i].variadic) { + varargs = []; + for (j = argIndex; j < argsLength; j++) { + varargs.push(args[j].value.eval(env)); + } + frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); + } else { + val = arg && arg.value; + if (val) { + val = val.eval(env); + } else if (params[i].value) { + val = params[i].value.eval(mixinEnv); + frame.resetCache(); + } else { + throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + + ' (' + argsLength + ' for ' + this.arity + ')' }; + } + + frame.prependRule(new(tree.Rule)(name, val)); + evaldArguments[i] = val; + } + } + + if (params[i].variadic && args) { + for (j = argIndex; j < argsLength; j++) { + evaldArguments[j] = args[j].value.eval(env); + } + } + argIndex++; + } + + return frame; + }, + eval: function (env) { + return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); + }, + evalCall: function (env, args, important) { + var _arguments = [], + mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, + frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), + rules, ruleset; + + frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); + + rules = this.rules.slice(0); + + ruleset = new(tree.Ruleset)(null, rules); + ruleset.originalRuleset = this; + ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); + if (important) { + ruleset = this.parent.makeImportant.apply(ruleset); + } + return ruleset; + }, + matchCondition: function (args, env) { + if (this.condition && !this.condition.eval( + new(tree.evalEnv)(env, + [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables + .concat(this.frames) // the parent namespace/mixin frames + .concat(env.frames)))) { // the current environment frames + return false; + } + return true; + }, + matchArgs: function (args, env) { + var argsLength = (args && args.length) || 0, len; + + if (! this.variadic) { + if (argsLength < this.required) { return false; } + if (argsLength > this.params.length) { return false; } + } else { + if (argsLength < (this.required - 1)) { return false; } + } + + len = Math.min(argsLength, this.arity); + + for (var i = 0; i < len; i++) { + if (!this.params[i].name && !this.params[i].variadic) { + if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { + return false; + } + } + } + return true; + } +}; +return Definition; +}; diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js deleted file mode 100644 index 506561bac..000000000 --- a/lib/less/tree/mixin.js +++ /dev/null @@ -1,315 +0,0 @@ -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index 12dbcc63c..6f3e9af8d 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.Negative = function (node) { +var Negative = function (node) { this.value = node; }; -tree.Negative.prototype = { +Negative.prototype = { type: "Negative", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -17,8 +17,8 @@ tree.Negative.prototype = { if (env.isMathOn()) { return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); } - return new(tree.Negative)(this.value.eval(env)); + return new(Negative)(this.value.eval(env)); } }; - -})(require('../tree')); +return Negative; +}; diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index 8f1dd6511..53dc37ba3 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.Operation = function (op, operands, isSpaced) { +var Operation = function (op, operands, isSpaced) { this.op = op.trim(); this.operands = operands; this.isSpaced = isSpaced; }; -tree.Operation.prototype = { +Operation.prototype = { type: "Operation", accept: function (visitor) { this.operands = visitor.visit(this.operands); @@ -28,7 +28,7 @@ tree.Operation.prototype = { return a.operate(env, this.op, b); } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); + return new(Operation)(this.op, [a, b], this.isSpaced); } }, genCSS: function (env, output) { @@ -45,6 +45,7 @@ tree.Operation.prototype = { toCSS: tree.toCSS }; +// todo move! tree.operate = function (env, op, a, b) { switch (op) { case '+': return a + b; @@ -53,5 +54,7 @@ tree.operate = function (env, op, a, b) { case '/': return a / b; } }; + +return Operation; -})(require('../tree')); +}; diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index e27b8d668..5c7dca864 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -1,10 +1,9 @@ +module.exports = function (tree) { -(function (tree) { - -tree.Paren = function (node) { +var Paren = function (node) { this.value = node; }; -tree.Paren.prototype = { +Paren.prototype = { type: "Paren", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -16,8 +15,8 @@ tree.Paren.prototype = { }, toCSS: tree.toCSS, eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); + return new(Paren)(this.value.eval(env)); } }; - -})(require('../tree')); +return Paren; +}; diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 131bfad90..738737a5a 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -1,13 +1,13 @@ -(function (tree) { +module.exports = function (tree) { -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { +var Quoted = function (str, content, escaped, index, currentFileInfo) { this.escaped = escaped; this.value = content || ''; this.quote = str.charAt(0); this.index = index; this.currentFileInfo = currentFileInfo; }; -tree.Quoted.prototype = { +Quoted.prototype = { type: "Quoted", genCSS: function (env, output) { if (!this.escaped) { @@ -44,5 +44,5 @@ tree.Quoted.prototype = { return left < right ? -1 : 1; } }; - -})(require('../tree')); +return Quoted; +}; diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index 4e1ccde69..f3cbcffb5 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { +var Rule = function (name, value, important, merge, index, currentFileInfo, inline) { this.name = name; this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); this.important = important ? ' ' + important.trim() : ''; @@ -11,7 +11,7 @@ tree.Rule = function (name, value, important, merge, index, currentFileInfo, inl this.variable = name.charAt && (name.charAt(0) === '@'); }; -tree.Rule.prototype = { +Rule.prototype = { type: "Rule", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -50,7 +50,7 @@ tree.Rule.prototype = { index: this.index, filename: this.currentFileInfo.filename }; } - return new(tree.Rule)(name, + return new(Rule)(name, evaldValue, this.important, this.merge, @@ -70,7 +70,7 @@ tree.Rule.prototype = { } }, makeImportant: function () { - return new(tree.Rule)(this.name, + return new(Rule)(this.name, this.value, "!important", this.merge, @@ -86,5 +86,7 @@ function evalName(env, name) { } return value; } + +return Rule; -})(require('../tree')); +}; diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js index a543c55e5..e7d998c43 100644 --- a/lib/less/tree/ruleset-call.js +++ b/lib/less/tree/ruleset-call.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.RulesetCall = function (variable) { +var RulesetCall = function (variable) { this.variable = variable; }; -tree.RulesetCall.prototype = { +RulesetCall.prototype = { type: "RulesetCall", accept: function (visitor) { }, @@ -12,5 +12,5 @@ tree.RulesetCall.prototype = { return detachedRuleset.callEval(env); } }; - -})(require('../tree')); +return RulesetCall; +}; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 190230045..76a8d2a76 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -1,12 +1,12 @@ -(function (tree) { +module.exports = function (tree) { -tree.Ruleset = function (selectors, rules, strictImports) { +var Ruleset = function (selectors, rules, strictImports) { this.selectors = selectors; this.rules = rules; this._lookups = {}; this.strictImports = strictImports; }; -tree.Ruleset.prototype = { +Ruleset.prototype = { type: "Ruleset", accept: function (visitor) { if (this.paths) { @@ -41,7 +41,7 @@ tree.Ruleset.prototype = { } var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), + ruleset = new(Ruleset)(selectors, rules, this.strictImports), rule, subRule; ruleset.originalRuleset = this; @@ -129,7 +129,7 @@ tree.Ruleset.prototype = { for (i = 0; i < rsRules.length; i++) { rule = rsRules[i]; // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { + if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) { // check if it can be folded in (e.g. & where) if (rule.selectors[0].isJustParentSelector()) { rsRules.splice(i--, 1); @@ -174,7 +174,7 @@ tree.Ruleset.prototype = { } }, makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { + return new Ruleset(this.selectors, this.rules.map(function (r) { if (r.makeImportant) { return r.makeImportant(); } else { @@ -221,13 +221,13 @@ tree.Ruleset.prototype = { rulesets: function () { if (!this.rules) { return null; } - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, + var _MixinDefinition = tree.mixin.Definition, filtRules = [], rules = this.rules, cnt = rules.length, i, rule; for (i = 0; i < cnt; i++) { rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { + if ((rule instanceof Ruleset) || (rule instanceof _MixinDefinition)) { filtRules.push(rule); } } @@ -551,4 +551,5 @@ tree.Ruleset.prototype = { } } }; -})(require('../tree')); +return Ruleset; +}; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index f38934e0e..ffcc8e830 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { +var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { this.elements = elements; this.extendList = extendList; this.condition = condition; @@ -10,7 +10,7 @@ tree.Selector = function (elements, extendList, condition, index, currentFileInf this.evaldCondition = true; } }; -tree.Selector.prototype = { +Selector.prototype = { type: "Selector", accept: function (visitor) { if (this.elements) { @@ -25,7 +25,7 @@ tree.Selector.prototype = { }, createDerived: function(elements, extendList, evaldCondition) { evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); + var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); newSelector.evaldCondition = evaldCondition; newSelector.mediaEmpty = this.mediaEmpty; return newSelector; @@ -125,5 +125,5 @@ tree.Selector.prototype = { return this.evaldCondition; } }; - -})(require('../tree')); +return Selector; +}; diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js index 7bf98ea56..674ac9830 100644 --- a/lib/less/tree/unicode-descriptor.js +++ b/lib/less/tree/unicode-descriptor.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.UnicodeDescriptor = function (value) { +var UnicodeDescriptor = function (value) { this.value = value; }; -tree.UnicodeDescriptor.prototype = { +UnicodeDescriptor.prototype = { type: "UnicodeDescriptor", genCSS: function (env, output) { output.add(this.value); @@ -11,5 +11,5 @@ tree.UnicodeDescriptor.prototype = { toCSS: tree.toCSS, eval: function () { return this; } }; - -})(require('../tree')); +return UnicodeDescriptor; +}; diff --git a/lib/less/tree/unit-conversions.js b/lib/less/tree/unit-conversions.js new file mode 100644 index 000000000..628f13dfe --- /dev/null +++ b/lib/less/tree/unit-conversions.js @@ -0,0 +1,21 @@ +module.exports = { + length: { + 'm': 1, + 'cm': 0.01, + 'mm': 0.001, + 'in': 0.0254, + 'px': 0.0254 / 96, + 'pt': 0.0254 / 72, + 'pc': 0.0254 / 72 * 12 + }, + duration: { + 's': 1, + 'ms': 0.001 + }, + angle: { + 'rad': 1/(2*Math.PI), + 'deg': 1/360, + 'grad': 1/400, + 'turn': 1 + } +}; \ No newline at end of file diff --git a/lib/less/tree/unit.js b/lib/less/tree/unit.js new file mode 100644 index 000000000..c25a7173a --- /dev/null +++ b/lib/less/tree/unit.js @@ -0,0 +1,137 @@ +module.exports = function(tree, unitConversions) { + +var Unit = function (numerator, denominator, backupUnit) { + this.numerator = numerator ? numerator.slice(0).sort() : []; + this.denominator = denominator ? denominator.slice(0).sort() : []; + this.backupUnit = backupUnit; +}; + +Unit.prototype = { + type: "Unit", + clone: function () { + return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); + }, + genCSS: function (env, output) { + if (this.numerator.length >= 1) { + output.add(this.numerator[0]); + } else + if (this.denominator.length >= 1) { + output.add(this.denominator[0]); + } else + if ((!env || !env.strictUnits) && this.backupUnit) { + output.add(this.backupUnit); + } + }, + toCSS: tree.toCSS, + + toString: function () { + var i, returnStr = this.numerator.join("*"); + for (i = 0; i < this.denominator.length; i++) { + returnStr += "/" + this.denominator[i]; + } + return returnStr; + }, + + compare: function (other) { + return this.is(other.toString()) ? 0 : -1; + }, + + is: function (unitString) { + return this.toString() === unitString; + }, + + isLength: function () { + return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); + }, + + isEmpty: function () { + return this.numerator.length === 0 && this.denominator.length === 0; + }, + + isSingular: function() { + return this.numerator.length <= 1 && this.denominator.length === 0; + }, + + map: function(callback) { + var i; + + for (i = 0; i < this.numerator.length; i++) { + this.numerator[i] = callback(this.numerator[i], false); + } + + for (i = 0; i < this.denominator.length; i++) { + this.denominator[i] = callback(this.denominator[i], true); + } + }, + + usedUnits: function() { + var group, result = {}, mapUnit; + + mapUnit = function (atomicUnit) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { + result[groupName] = atomicUnit; + } + + return atomicUnit; + }; + + for (var groupName in unitConversions) { + if (unitConversions.hasOwnProperty(groupName)) { + group = unitConversions[groupName]; + + this.map(mapUnit); + } + } + + return result; + }, + + cancel: function () { + var counter = {}, atomicUnit, i, backup; + + for (i = 0; i < this.numerator.length; i++) { + atomicUnit = this.numerator[i]; + if (!backup) { + backup = atomicUnit; + } + counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; + } + + for (i = 0; i < this.denominator.length; i++) { + atomicUnit = this.denominator[i]; + if (!backup) { + backup = atomicUnit; + } + counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; + } + + this.numerator = []; + this.denominator = []; + + for (atomicUnit in counter) { + if (counter.hasOwnProperty(atomicUnit)) { + var count = counter[atomicUnit]; + + if (count > 0) { + for (i = 0; i < count; i++) { + this.numerator.push(atomicUnit); + } + } else if (count < 0) { + for (i = 0; i < -count; i++) { + this.denominator.push(atomicUnit); + } + } + } + } + + if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { + this.backupUnit = backup; + } + + this.numerator.sort(); + this.denominator.sort(); + } +}; +return Unit; +}; \ No newline at end of file diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 905626856..28e37d2a3 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.URL = function (val, currentFileInfo, isEvald) { +var URL = function (val, currentFileInfo, isEvald) { this.value = val; this.currentFileInfo = currentFileInfo; this.isEvald = isEvald; }; -tree.URL.prototype = { +URL.prototype = { type: "Url", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -46,8 +46,8 @@ tree.URL.prototype = { } } - return new(tree.URL)(val, this.currentFileInfo, true); + return new(URL)(val, this.currentFileInfo, true); } }; - -})(require('../tree')); +return URL; +}; diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index f95e7b580..ea07367b7 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.Value = function (value) { +var Value = function (value) { this.value = value; }; -tree.Value.prototype = { +Value.prototype = { type: "Value", accept: function (visitor) { if (this.value) { @@ -14,7 +14,7 @@ tree.Value.prototype = { if (this.value.length === 1) { return this.value[0].eval(env); } else { - return new(tree.Value)(this.value.map(function (v) { + return new(Value)(this.value.map(function (v) { return v.eval(env); })); } @@ -30,5 +30,5 @@ tree.Value.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Value; +}; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 90d862718..1e152e4c1 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -1,17 +1,17 @@ -(function (tree) { +module.exports = function (tree) { -tree.Variable = function (name, index, currentFileInfo) { +var Variable = function (name, index, currentFileInfo) { this.name = name; this.index = index; this.currentFileInfo = currentFileInfo || {}; }; -tree.Variable.prototype = { +Variable.prototype = { type: "Variable", eval: function (env) { var variable, name = this.name; if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; + name = '@' + new(Variable)(name.slice(1)).eval(env).value; } if (this.evaluating) { @@ -40,5 +40,5 @@ tree.Variable.prototype = { } } }; - -})(require('../tree')); +return Variable; +}; diff --git a/lib/less/visitor.js b/lib/less/visitor.js index 75b9975a2..d4696e725 100644 --- a/lib/less/visitor.js +++ b/lib/less/visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { var _visitArgs = { visitDeeper: true }, _hasIndexed = false; @@ -143,4 +143,4 @@ } }; -})(require('./tree')); \ No newline at end of file +}; diff --git a/package.json b/package.json index 8e91bbfac..95ccd88de 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "http-server": "~0.6.1", "matchdep": "~0.3.0", "time-grunt": "~0.2.9", - "grunt-contrib-copy": "~0.4.1" + "browserify": "~3.30.2", + "grunt-browserify": "~1.3.1" }, "keywords": [ "compile less", From 462b0274b224c994b764a4c12acbd4cd8f44f700 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 24 Feb 2014 21:28:21 +0000 Subject: [PATCH 018/161] move environment to the less namespace --- lib/less/browser.js | 4 ++-- lib/less/environments/browser.js | 2 +- lib/less/functions.js | 2 +- lib/less/index.js | 2 +- lib/less/parser.js | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/less/browser.js b/lib/less/browser.js index 93b3f84ea..a0c65d0bb 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -340,10 +340,10 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { env.useFileCache = true; } - less.Parser.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { + less.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { var newFileInfo = { - currentDirectory: less.Parser.environment.getPath(env, path), + currentDirectory: less.environment.getPath(env, path), filename: path, rootFilename: path, relativeUrls: env.relativeUrls}; diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js index ae45d1199..216b4c0cb 100644 --- a/lib/less/environments/browser.js +++ b/lib/less/environments/browser.js @@ -19,7 +19,7 @@ function getXMLHttpRequest() { } } -less.Parser.environment = { +less.environment = { // make generic but overriddable warn: function warn(env, msg) { console.warn(msg); diff --git a/lib/less/functions.js b/lib/less/functions.js index 8fac47aa1..47cf3304e 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -530,7 +530,7 @@ var functions = { if (useBase64) { try { - returner = less.Parser.environment.encodeBase64(this.env, returner); + returner = less.environment.encodeBase64(this.env, returner); } catch(e) { useBase64 = false; } diff --git a/lib/less/index.js b/lib/less/index.js index a2c69175c..ea47acbf8 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -90,6 +90,6 @@ less.writeError = function (ctx, options) { console.error(less.formatError(ctx, options)); }; -less.Parser.environment = require("./environments/node"); +less.environment = require("./environments/node"); module.exports = less; diff --git a/lib/less/parser.js b/lib/less/parser.js index 8c94f8bdd..af231160d 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -82,7 +82,7 @@ var Parser = function Parser(env) { rootFilename: currentFileInfo.rootFilename }; - less.Parser.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { + less.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { if (e) { fileParsedFunc(e); return; @@ -96,11 +96,11 @@ var Parser = function Parser(env) { // then rootpath should become 'less/module/nav/' // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' - newFileInfo.currentDirectory = less.Parser.environment.getPath(env, resolvedFilename); + newFileInfo.currentDirectory = less.environment.getPath(env, resolvedFilename); if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = less.Parser.environment.join((env.rootpath || ""), less.Parser.environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); - if (!less.Parser.environment.isPathAbsolute(env, newFileInfo.rootpath) && less.Parser.environment.alwaysMakePathsAbsolute()) { - newFileInfo.rootpath = less.Parser.environment.join(newFileInfo.entryPath, newFileInfo.rootpath); + newFileInfo.rootpath = less.environment.join((env.rootpath || ""), less.environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!less.environment.isPathAbsolute(env, newFileInfo.rootpath) && less.environment.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = less.environment.join(newFileInfo.entryPath, newFileInfo.rootpath); } } newFileInfo.filename = resolvedFilename; From ddf1a45e5eb572d72255acfe36cd8b03ec0d3141 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 24 Feb 2014 21:51:38 +0000 Subject: [PATCH 019/161] continue moving node functionality into the environment interface. function names need making consistent etc. --- lib/less/environments/api.js | 2 ++ lib/less/environments/node.js | 18 +++++++++++++ lib/less/functions.js | 50 +++++------------------------------ lib/less/parser.js | 10 +++---- 4 files changed, 31 insertions(+), 49 deletions(-) diff --git a/lib/less/environments/api.js b/lib/less/environments/api.js index 483895f3f..74f0dcc0b 100644 --- a/lib/less/environments/api.js +++ b/lib/less/environments/api.js @@ -36,5 +36,7 @@ module.exports = { * @returns {Boolean} */ loadFile: function(env, filename, currentDirectory, callback) { + }, + supportsDataURI: function(env) { } }; \ No newline at end of file diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js index a4cae9be2..ea4f24085 100644 --- a/lib/less/environments/node.js +++ b/lib/less/environments/node.js @@ -11,6 +11,21 @@ module.exports = { encodeBase64: function encodeBase64(env, str) { return new Buffer(str).toString('base64'); }, + supportsDataURI: function(env) { + return true; + }, + mimeLookup: function (env, filename) { + return require('mime').lookup(filename); + }, + charsetLookup: function (env, mime) { + return require('mime').charsets.lookup(mime); + }, + readFileSync: function (filename) { + return require("fs").readFileSync(filename); + }, + getCleanCSS: function() { + return require('clean-css'); + }, getPath: function (env, filename) { var j = filename.lastIndexOf('/'); if (j < 0) { @@ -24,6 +39,9 @@ module.exports = { isPathAbsolute: function(env, filename) { return (/^(?:[a-z-]+:|\/|\\)/).test(filename); }, + getAbsolutePath: function getAbsolutePath(env, filename) { + return require('path').resolve(filename); + }, alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { return false; }, diff --git a/lib/less/functions.js b/lib/less/functions.js index 47cf3304e..d0cf83586 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -397,16 +397,14 @@ var functions = { "data-uri": function(mimetypeNode, filePathNode) { - if (typeof window !== 'undefined') { + if (!less.environment.supportsDataURI(this.env)) { return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); } var mimetype = mimetypeNode.value; var filePath = (filePathNode && filePathNode.value); - var fs = require('fs'), - path = require('path'), - useBase64 = false; + var useBase64 = false; if (arguments.length < 2) { filePath = mimetype; @@ -414,25 +412,19 @@ var functions = { if (this.env.isPathRelative(filePath)) { if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); + filePath = less.environment.join(this.currentFileInfo.currentDirectory, filePath); } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); + filePath = less.environment.join(this.currentFileInfo.entryPath, filePath); } } // detect the mimetype if not given if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - mimetype = mime.lookup(filePath); + mimetype = less.environment.mimeLookup(this.env, filePath); // use base 64 unless it's an ASCII or UTF-8 format - var charset = mime.charsets.lookup(mimetype); + var charset = less.environment.charsetLookup(this.env, mimetype); useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; if (useBase64) { mimetype += ';base64'; } } @@ -440,7 +432,7 @@ var functions = { useBase64 = /;base64$/.test(mimetype); } - var buf = fs.readFileSync(filePath); + var buf = less.environment.readFileSync(filePath); // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded // and the --ieCompat flag is enabled, return a normal url() instead. @@ -541,34 +533,6 @@ var functions = { } }; -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - // Math var mathFunctions = { diff --git a/lib/less/parser.js b/lib/less/parser.js index af231160d..ee565994b 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -312,9 +312,7 @@ var Parser = function Parser(env) { function getDebugInfo(index, inputStream, env) { var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } + filename = less.environment.getAbsolutePath(env, filename); return { lineNumber: getLocation(index, inputStream).line + 1, @@ -614,9 +612,9 @@ var Parser = function Parser(env) { throw new(LessError)(e, env); } - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; + var CleanCSS = less.environment.getCleanCSS(); + if (options.cleancss && CleanCSS) { + var cleancssOptions = options.cleancssOptions || {}; if (cleancssOptions.keepSpecialComments === undefined) { cleancssOptions.keepSpecialComments = "*"; From e30cccc508d0dde7479d55a9f660a647c60b3cf8 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 26 Feb 2014 19:30:37 +0000 Subject: [PATCH 020/161] move sourcemap generator to environment --- lib/less/environments/node.js | 3 +++ lib/less/non-node-index.js | 2 +- lib/less/source-map-output.js | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js index ea4f24085..d4ef9c3c9 100644 --- a/lib/less/environments/node.js +++ b/lib/less/environments/node.js @@ -42,6 +42,9 @@ module.exports = { getAbsolutePath: function getAbsolutePath(env, filename) { return require('path').resolve(filename); }, + getSourceMapGenerator: function getSourceMapGenerator() { + return require("source-map").SourceMapGenerator; + }, alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { return false; }, diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 5c3f8c7ec..5cd0ad163 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -13,6 +13,6 @@ require('./import-visitor.js')(less.tree); require('./extend-visitor.js')(less.tree); require('./join-selector-visitor.js')(less.tree); require('./to-css-visitor.js')(less.tree); -require('./source-map-output.js')(less.tree); +require('./source-map-output.js')(less, less.tree); module.exports = less; diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 2fd10ec2b..9cdc8181b 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +module.exports = function (less, tree) { tree.sourceMapOutput = function (options) { this._css = []; @@ -14,7 +14,7 @@ module.exports = function (tree) { } this._sourceMapRootpath = options.sourceMapRootpath; this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; + this._sourceMapGeneratorConstructor = less.environment.getSourceMapGenerator(); if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { this._sourceMapRootpath += '/'; From 37cd664a8855225dc2822d327ec35d6a1f420860 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 27 Feb 2014 21:21:01 +0000 Subject: [PATCH 021/161] remove merged in dist files --- dist/less-1.7.0.js | 7921 ------------------------------- dist/less-1.7.0.min.js | 16 - dist/less-rhino-1.7.0.js | 9301 ------------------------------------- dist/lessc-rhino-1.7.0.js | 449 -- 4 files changed, 17687 deletions(-) delete mode 100644 dist/less-1.7.0.js delete mode 100644 dist/less-1.7.0.min.js delete mode 100644 dist/less-rhino-1.7.0.js delete mode 100644 dist/lessc-rhino-1.7.0.js diff --git a/dist/less-1.7.0.js b/dist/less-1.7.0.js deleted file mode 100644 index 6ff1bc604..000000000 --- a/dist/less-1.7.0.js +++ /dev/null @@ -1,7921 +0,0 @@ -/*! - * LESS - Leaner CSS v1.7.0 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p; - if (env && (env.numPrecision != null)) { - p = Math.pow(10, env.numPrecision); - return Math.round(value * p) / p; - } else { - return value; - } -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (css.styleSheet) { // IE - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } else { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; - } - return styles; -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject)) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.7.0.min.js b/dist/less-1.7.0.min.js deleted file mode 100644 index 3cecd957d..000000000 --- a/dist/less-1.7.0.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * LESS - Leaner CSS v1.7.0 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");if(j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet)try{j.styleSheet.cssText=a}catch(k){throw new Error("Couldn't reassign styleSheet.cssText.")}else j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue;var l=document.getElementsByTagName("head")[0];if(null===h||i===!1){var m=b&&b.nextSibling||null;m?m.parentNode.insertBefore(j,m):l.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(k){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;dD&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,g=y;d();var h=j(/^@import?\s+/),i=(h?this.importOptions():null)||{};return h&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),k(";"))?(f(),c=c&&new x.Value(c),new x.Import(b,c,i,g,a.currentFileInfo)):void e()},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(c=k("/")||k("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(y+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=C,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(Math.round(b*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c;return a&&null!=a.numPrecision?(c=Math.pow(10,a.numPrecision),Math.round(b*c)/c):b},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a.value||a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb?a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)s.value(h),r[h]=d.matchCondition(e,b);(r[0]||r[1])&&(r[0]!=r[1]&&(l.group=r[1]?u:v),q.push(l))}else q.push(l);p=!0}}for(s.reset(),n=[0,0,0],g=0;g0)m=v;else if(m=u,n[u]+n[v]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gh;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;ii;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames.concat(c.frames)),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value||c instanceof a.Ruleset?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d,e=!1,f=this.name;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:b(c,f)),"font"!==f||c.strictMath||(e=!0,c.strictMath=!0);try{if(d=this.value.eval(c),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(g){throw"number"!=typeof g.index&&(g.index=this.index,g.filename=this.currentFileInfo.filename),g}finally{e&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;fb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),h.importMultiple||(b.skip=g?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};if(d){b.root=d,b.importedFilename=i;var k=g||i in e.recursionDetector;if(!f&&(h.importMultiple||!k))return e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,j,h,e.onceFileDetectionMap,e.recursionDetector).run(d)}j()})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++); -return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0) { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p; - if (env && (env.numPrecision != null)) { - p = Math.pow(10, env.numPrecision); - return Math.round(value * p) / p; - } else { - return value; - } -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { - this.value = string.value || string; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/lessc-rhino-1.7.0.js b/dist/lessc-rhino-1.7.0.js deleted file mode 100644 index 7f5884da0..000000000 --- a/dist/lessc-rhino-1.7.0.js +++ /dev/null @@ -1,449 +0,0 @@ -/* LESS.js v1.7.0 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - print(formatError(ctx, options)); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } - console.log("done"); -}(arguments)); From 3229b45fea3d4f3e5a4aef3f113614138a14dec3 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 27 Feb 2014 22:25:51 +0000 Subject: [PATCH 022/161] get the browser file running and processing less (still doesnt pass tests though) --- Gruntfile.js | 10 ++++- lib/less/browser.js | 66 +++++++++++++++++--------------- lib/less/environments/browser.js | 8 +++- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f83ad14d0..8caa6cb0d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -51,7 +51,12 @@ module.exports = function(grunt) { browserify: { dist: { files: { - 'dist/less.js': ['lib/less/non-node-index.js', 'lib/less/browser.js', 'lib/less/environment/browser.js'] + 'dist/less.js': ['lib/less/browser.js'] + } + }, + browsertest: { + files: { + 'test/browser/less.js': ['lib/less/browser.js'] } } }, @@ -274,12 +279,13 @@ module.exports = function(grunt) { // setup a web server to run the browser tests in a browser rather than phantom grunt.registerTask('browsertest-server', [ + 'browser', 'shell:browsertest-server' ]); // Create the browser version of less.js grunt.registerTask('browser', [ - 'concat:browsertest' + 'browserify:browsertest' ]); // Run all tests diff --git a/lib/less/browser.js b/lib/less/browser.js index a0c65d0bb..3138c5740 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -1,11 +1,23 @@ // // browser.js - client-side engine // -/*global less, window, document, location */ +/*global window, document, location */ + +var less = require('./non-node-index.js'), + options = window.less; + +function log(str, level) { + if (typeof(console) !== 'undefined' && less.logLevel >= level) { + console.log('less: ' + str); + } +} var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); -less.env = less.env || (location.hostname == '127.0.0.1' || +less.environment = require("./environments/browser.js")(less, isFileProtocol, log, 1/*TODO*/); +window.less = less; + +less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || location.hostname == 'localhost' || (location.port && @@ -26,7 +38,7 @@ var logLevel = { // 1 - Errors // 0 - None // Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); +less.logLevel = typeof(options.logLevel) != 'undefined' ? options.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); // Load styles asynchronously (default: false) // @@ -41,10 +53,10 @@ less.fileAsync = less.fileAsync || false; less.poll = less.poll || (isFileProtocol ? 1000 : 1500); //Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; +if (options.functions) { + for(var func in options.functions) { + if (options.functions.hasOwnProperty(func)) { + less.tree.functions[func] = options.functions[func]; } } } @@ -57,12 +69,6 @@ if (dumpLineNumbers) { var typePattern = /^text\/(x-)?less$/; var cache = null; -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - function extractId(href) { return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain .replace(/^\//, '' ) // Remove root / @@ -161,8 +167,8 @@ function createCSS(styles, sheet, lastModified) { } function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; + if (options.postProcessor && typeof options.postProcessor === 'function') { + styles = options.postProcessor.call(styles, styles) || styles; } return styles; } @@ -266,12 +272,12 @@ function errorHTML(e, rootHref) { } function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { + if (!options.errorReporting || options.errorReporting === "html") { errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { + } else if (options.errorReporting === "console") { errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("add", e, rootHref); } } @@ -287,12 +293,12 @@ function removeErrorConsole(path) { } function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { + if (!options.errorReporting || options.errorReporting === "html") { removeErrorHTML(path); - } else if (less.errorReporting === "console") { + } else if (options.errorReporting === "console") { removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("remove", path); } } @@ -306,7 +312,7 @@ function loadStyles(modifyVars) { lessText = style.innerHTML || ''; env.filename = document.location.href.replace(/#.*$/, ''); - if (modifyVars || less.globalVars) { + if (modifyVars || options.globalVars) { env.useFileCache = true; } @@ -317,7 +323,7 @@ function loadStyles(modifyVars) { if (e) { return error(e, "inline"); } - var css = cssAST.toCSS(less); + var css = cssAST.toCSS(options); style.type = 'text/css'; if (style.styleSheet) { style.styleSheet.cssText = css; @@ -326,7 +332,7 @@ function loadStyles(modifyVars) { } }; })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); + new(less.Parser)(env).parse(lessText, callback, {globalVars: options.globalVars, modifyVars: modifyVars}); } } } @@ -336,7 +342,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { var env = new less.tree.parseEnv(less); env.mime = sheet.type; - if (modifyVars || less.globalVars) { + if (modifyVars || options.globalVars) { env.useFileCache = true; } @@ -380,7 +386,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { } catch (e) { callback(e, null, null, sheet); } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); + }, {modifyVars: modifyVars, globalVars: options.globalVars}); } else { callback(e, null, null, sheet, webInfo, path); } @@ -407,7 +413,7 @@ function initRunningMode(){ } }); } - }, less.poll); + }, options.poll); } } @@ -469,7 +475,7 @@ less.refresh = function (reload, modifyVars) { log("loading " + sheet.href + " from cache.", logLevel.info); } else { log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); + var styles = root.toCSS(options); styles = postProcessCSS(styles); createCSS(styles, sheet, env.lastModified); } diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js index 216b4c0cb..15a967f60 100644 --- a/lib/less/environments/browser.js +++ b/lib/less/environments/browser.js @@ -1,4 +1,6 @@ -/*global less, window, XMLHttpRequest, log, logLevel, isFileProtocol */ +/*global window, XMLHttpRequest */ + +module.exports = function(less, isFileProtocol, log, logLevel) { var fileCache = {}; @@ -19,7 +21,7 @@ function getXMLHttpRequest() { } } -less.environment = { +return { // make generic but overriddable warn: function warn(env, msg) { console.warn(msg); @@ -193,4 +195,6 @@ less.environment = { }); } +}; + }; \ No newline at end of file From 418af74c278bd8e360df6ea334f84547c7264cd0 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Fri, 28 Feb 2014 18:07:56 +0000 Subject: [PATCH 023/161] get most of the browser tests running --- lib/less/browser.js | 20 ++++++++++---------- lib/less/environments/browser.js | 3 +++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/less/browser.js b/lib/less/browser.js index 3138c5740..6ba6fa75d 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -6,6 +6,13 @@ var less = require('./non-node-index.js'), options = window.less; +var logLevel = { + debug: 3, + info: 2, + errors: 1, + none: 0 +}; + function log(str, level) { if (typeof(console) !== 'undefined' && less.logLevel >= level) { console.log('less: ' + str); @@ -14,7 +21,7 @@ function log(str, level) { var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); -less.environment = require("./environments/browser.js")(less, isFileProtocol, log, 1/*TODO*/); +less.environment = require("./environments/browser.js")(less, isFileProtocol, log, logLevel); window.less = less; less.env = options.env || (location.hostname == '127.0.0.1' || @@ -25,13 +32,6 @@ less.env = options.env || (location.hostname == '127.0.0.1' || isFileProtocol ? 'development' : 'production'); -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - // The amount of logging in the javascript console. // 3 - Debug, information and errors // 2 - Information and errors @@ -308,7 +308,7 @@ function loadStyles(modifyVars) { for (var i = 0; i < styles.length; i++) { style = styles[i]; if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), + var env = new less.tree.parseEnv(options), lessText = style.innerHTML || ''; env.filename = document.location.href.replace(/#.*$/, ''); @@ -339,7 +339,7 @@ function loadStyles(modifyVars) { function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - var env = new less.tree.parseEnv(less); + var env = new less.tree.parseEnv(options); env.mime = sheet.type; if (modifyVars || options.globalVars) { diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js index 15a967f60..a8fe835bf 100644 --- a/lib/less/environments/browser.js +++ b/lib/less/environments/browser.js @@ -44,6 +44,8 @@ return { alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { return true; }, + getCleanCSS: function () { + }, pathDiff: function pathDiff(url, baseUrl) { // diff between two paths to create a relative path @@ -128,6 +130,7 @@ return { return returner; }, doXHR: function doXHR(url, type, callback, errback) { + var xhr = getXMLHttpRequest(); var async = isFileProtocol ? less.fileAsync : less.async; From 2ac8617987b7fb3bc45273b3a83fa38282ecd6bf Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 1 Mar 2014 17:25:01 +0000 Subject: [PATCH 024/161] Fix the browser tests --- lib/less/environments/browser.js | 3 +++ test/browser/runner-browser-options.js | 3 ++- test/browser/runner-errors-options.js | 4 ++-- test/browser/runner-global-vars-options.js | 10 ++++++---- test/browser/runner-legacy-options.js | 8 +++++--- test/browser/runner-main-options.js | 5 ++++- test/browser/runner-modify-vars-options.js | 5 ++++- test/browser/runner-no-js-errors-options.js | 2 +- test/browser/runner-postProcessor-options.js | 3 ++- test/browser/runner-production-options.js | 3 ++- test/browser/runner-relative-urls-options.js | 3 ++- test/browser/runner-rootpath-options.js | 3 ++- test/browser/runner-rootpath-relative-options.js | 3 ++- 13 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/less/environments/browser.js b/lib/less/environments/browser.js index a8fe835bf..26b74cf58 100644 --- a/lib/less/environments/browser.js +++ b/lib/less/environments/browser.js @@ -46,6 +46,9 @@ return { }, getCleanCSS: function () { }, + supportsDataURI: function() { + return false; + }, pathDiff: function pathDiff(url, baseUrl) { // diff between two paths to create a relative path diff --git a/test/browser/runner-browser-options.js b/test/browser/runner-browser-options.js index 3d73323e7..6eadfd60c 100644 --- a/test/browser/runner-browser-options.js +++ b/test/browser/runner-browser-options.js @@ -1,4 +1,4 @@ -var less = {}; +var less = {logLevel: 4, errorReporting: "console"}; // There originally run inside describe method. However, since they have not // been inside it, they run at jasmine compile time (not runtime). It all @@ -11,6 +11,7 @@ var testFiles = ['charsets', 'colors', 'comments', 'css-3', 'strings', 'media', testSheets = []; // setup style tags with less and link tags pointing to expected css output + for (var i = 0; i < testFiles.length; i++) { var file = testFiles[i], lessPath = '/test/less/' + file + '.less', diff --git a/test/browser/runner-errors-options.js b/test/browser/runner-errors-options.js index 840ba5d63..1ed092ee1 100644 --- a/test/browser/runner-errors-options.js +++ b/test/browser/runner-errors-options.js @@ -1,5 +1,5 @@ var less = { strictUnits: true, - strictMath: true -}; + strictMath: true, + logLevel: 4 }; diff --git a/test/browser/runner-global-vars-options.js b/test/browser/runner-global-vars-options.js index f313b2f77..859751a4d 100644 --- a/test/browser/runner-global-vars-options.js +++ b/test/browser/runner-global-vars-options.js @@ -1,4 +1,6 @@ -var less = {}; -less.globalVars = { - "@global-var": "red" -}; +var less = { + logLevel: 4, + errorReporting: "console", + globalVars: { + "@global-var": "red" +}}; diff --git a/test/browser/runner-legacy-options.js b/test/browser/runner-legacy-options.js index 9e4b95921..eb53e3534 100644 --- a/test/browser/runner-legacy-options.js +++ b/test/browser/runner-legacy-options.js @@ -1,4 +1,6 @@ -var less = {}; -less.strictMath = false; -less.strictUnits = false; +var less = { + logLevel: 4, + errorReporting: "console", + strictMath: false, + strictUnits: false }; diff --git a/test/browser/runner-main-options.js b/test/browser/runner-main-options.js index df51782a8..f8e0b8b2e 100644 --- a/test/browser/runner-main-options.js +++ b/test/browser/runner-main-options.js @@ -1,4 +1,7 @@ -var less = {}; +var less = { + logLevel: 4, + errorReporting: "console" +}; less.strictMath = true; less.functions = { add: function(a, b) { diff --git a/test/browser/runner-modify-vars-options.js b/test/browser/runner-modify-vars-options.js index 2799ad55d..1516f5495 100644 --- a/test/browser/runner-modify-vars-options.js +++ b/test/browser/runner-modify-vars-options.js @@ -1,2 +1,5 @@ /* exported less */ -var less = {}; \ No newline at end of file +var less = { + logLevel: 4, + errorReporting: "console" +}; \ No newline at end of file diff --git a/test/browser/runner-no-js-errors-options.js b/test/browser/runner-no-js-errors-options.js index 2a4649447..825de5cc3 100644 --- a/test/browser/runner-no-js-errors-options.js +++ b/test/browser/runner-no-js-errors-options.js @@ -1,4 +1,4 @@ -var less = {}; +var less = {logLevel: 4}; less.strictUnits = true; less.javascriptEnabled = false; diff --git a/test/browser/runner-postProcessor-options.js b/test/browser/runner-postProcessor-options.js index 94044da21..fe7111b69 100644 --- a/test/browser/runner-postProcessor-options.js +++ b/test/browser/runner-postProcessor-options.js @@ -1,4 +1,5 @@ -var less = {}; +var less = {logLevel: 4, + errorReporting: "console"}; less.postProcessor = function(styles) { return 'hr {height:50px;}\n' + styles; }; diff --git a/test/browser/runner-production-options.js b/test/browser/runner-production-options.js index f8189d6a8..2737aba2c 100644 --- a/test/browser/runner-production-options.js +++ b/test/browser/runner-production-options.js @@ -1,3 +1,4 @@ -var less = {}; +var less = {logLevel: 4, + errorReporting: "console"}; less.env = "production"; diff --git a/test/browser/runner-relative-urls-options.js b/test/browser/runner-relative-urls-options.js index a20eb591e..3dcbad197 100644 --- a/test/browser/runner-relative-urls-options.js +++ b/test/browser/runner-relative-urls-options.js @@ -1,3 +1,4 @@ -var less = {}; +var less = {logLevel: 4, + errorReporting: "console"}; less.relativeUrls = true; diff --git a/test/browser/runner-rootpath-options.js b/test/browser/runner-rootpath-options.js index 65cb1f14c..f1aa52c4a 100644 --- a/test/browser/runner-rootpath-options.js +++ b/test/browser/runner-rootpath-options.js @@ -1,3 +1,4 @@ -var less = {}; +var less = {logLevel: 4, + errorReporting: "console"}; less.rootpath = "https://localhost/"; diff --git a/test/browser/runner-rootpath-relative-options.js b/test/browser/runner-rootpath-relative-options.js index f97baa4b8..df4d883c2 100644 --- a/test/browser/runner-rootpath-relative-options.js +++ b/test/browser/runner-rootpath-relative-options.js @@ -1,4 +1,5 @@ -var less = {}; +var less = {logLevel: 4, + errorReporting: "console"}; less.rootpath = "https://www.github.com/cloudhead/less.js/"; less.relativeUrls = true; From a8ded64edbbf2bbfc24bed1d1003e8ed0fc98c04 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 2 Mar 2014 09:47:32 +0000 Subject: [PATCH 025/161] add banner and setup dist to generate browser output from browserify --- Gruntfile.js | 67 +++++++++++++++++++++++-------------------------- bin/lessc | 21 ++++++++++------ build/build.yml | 32 ----------------------- 3 files changed, 45 insertions(+), 75 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8caa6cb0d..ff09d0fed 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -49,30 +49,22 @@ module.exports = function(grunt) { }, browserify: { - dist: { - files: { - 'dist/less.js': ['lib/less/browser.js'] - } - }, - browsertest: { - files: { - 'test/browser/less.js': ['lib/less/browser.js'] - } + browser: { + src: ['./lib/less/browser.js'], + dest: 'tmp/less.js' } }, concat: { options: { stripBanners: 'all', - banner: '<%= meta.banner %>\n\n(function (window, undefined) {', - footer: '\n})(window);' + banner: '<%= meta.banner %>' }, - // Browser versions browsertest: { - src: ['<%= build.browser %>'], + src: '<%= browserify.browser.dest %>', dest: 'test/browser/less.js' }, - stable: { - src: ['<%= build.browser %>'], + dist: { + src: '<%= browserify.browser.dest %>', dest: 'dist/less.js' }, // Rhino @@ -107,8 +99,8 @@ module.exports = function(grunt) { banner: '<%= meta.banner %>', mangle: true }, - stable: { - src: ['<%= concat.stable.dest %>'], + dist: { + src: ['<%= concat.dist.dest %>'], dest: 'dist/less.min.js' } }, @@ -118,7 +110,8 @@ module.exports = function(grunt) { files: { src: [ 'Gruntfile.js', - 'lib/less/**/*.js' + 'lib/less/**/*.js', + 'bin/lessc' ] } }, @@ -257,37 +250,46 @@ module.exports = function(grunt) { 'test' ]); + grunt.registerTask('updateBowerJson', function() { + var bowerJson = require('./bower.json'); + bowerJson.version = grunt.config('pkg.version'); + fs.writeFileSync('./bower.json', JSON.stringify(bowerJson,null,2)); + }); + // Release - grunt.registerTask('stable', [ - 'concat:stable', - 'uglify:stable', + grunt.registerTask('dist', [ + 'browserify:browser', + 'concat:dist', + 'uglify:dist', 'updateBowerJson' ]); // Release Rhino Version grunt.registerTask('rhino', [ + 'browserify:rhino', 'concat:rhino', 'concat:rhinolessc' ]); - - // Run all browser tests + + // Create the browser version of less.js + grunt.registerTask('browsertest-lessjs', [ + 'browserify:browser', + 'concat:browsertest' + ]); + + // Run all browser tests grunt.registerTask('browsertest', [ - 'browser', + 'browsertest-lessjs', 'connect', 'jasmine' ]); // setup a web server to run the browser tests in a browser rather than phantom grunt.registerTask('browsertest-server', [ - 'browser', + 'browsertest-less', 'shell:browsertest-server' ]); - // Create the browser version of less.js - grunt.registerTask('browser', [ - 'browserify:browsertest' - ]); - // Run all tests grunt.registerTask('test', [ 'clean', @@ -312,9 +314,4 @@ module.exports = function(grunt) { 'concat:readme' ]); - grunt.registerTask('updateBowerJson', function() { - var bowerJson = require('./bower.json'); - bowerJson.version = grunt.config('pkg.version'); - fs.writeFileSync('./bower.json', JSON.stringify(bowerJson,null,2)); - }); }; diff --git a/bin/lessc b/bin/lessc index c1abb78f3..16620aabb 100755 --- a/bin/lessc +++ b/bin/lessc @@ -35,7 +35,7 @@ var continueProcessing = true, // calling process.exit does not flush stdout always // so use this to set the exit code -process.on('exit', function() { process.reallyExit(currentErrorcode) }); +process.on('exit', function() { process.reallyExit(currentErrorcode); }); var checkArgFunc = function(arg, option) { if (!option) { @@ -67,19 +67,22 @@ var sourceMapFileInline = false; args = args.filter(function (arg) { var match; - if (match = arg.match(/^-I(.+)$/)) { + match = arg.match(/^-I(.+)$/); + if (match) { options.paths.push(match[1]); return false; } - if (match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i)) { arg = match[1] } - else { return arg } + match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); + if (match) { arg = match[1]; } + else { return arg; } switch (arg) { case 'v': case 'version': console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]"); continueProcessing = false; + break; case 'verbose': options.verbose = true; break; @@ -98,6 +101,7 @@ args = args.filter(function (arg) { case 'help': require('../lib/less/lessc_helper').printUsage(); continueProcessing = false; + break; case 'x': case 'compress': options.compress = true; @@ -257,7 +261,6 @@ if (!continueProcessing) { } var input = args[1]; -var inputbase = args[1]; if (input && input != '-') { input = path.resolve(process.cwd(), input); } @@ -345,12 +348,14 @@ var parseLessFile = function (e, data) { return; } else if (options.depends) { for(var file in parser.imports.files) { - sys.print(file + " ") + if (parser.imports.files.hasOwnProperty(file)) { + sys.print(file + " "); + } } sys.print("\n"); } else { try { - if (options.lint) { writeSourceMap = function() {} } + if (options.lint) { writeSourceMap = function() {}; } var css = tree.toCSS({ silent: options.silent, verbose: options.verbose, @@ -380,7 +385,7 @@ var parseLessFile = function (e, data) { } } else { sys.print(css); - } + } } } catch (e) { less.writeError(e, options); diff --git a/build/build.yml b/build/build.yml index 098901e44..c179f936d 100644 --- a/build/build.yml +++ b/build/build.yml @@ -19,12 +19,10 @@ lib_source_map: 'lib/source-map' # General # ================================= prepend: - browser: ['build/require.js', 'build/browser-header.js'] rhino: ['build/require-rhino.js', 'build/rhino-header.js', 'build/rhino-modules.js'] append: amd: build/amd.js - browser: ['lib/less/environments/browser.js', '<%= build.lib %>/browser.js'] rhino: <%= build.lib %>/rhino.js @@ -48,36 +46,6 @@ less: browser : <%= build.lib %>/browser.js source_map_output: <%= build.lib %>/source-map-output.js - -# ================================= -# Browser build -# ================================= - -# <%= build.browser %> -browser: - - # prepend utils - - <%= build.prepend.browser %> - - # core - - <%= build.less.parser %> - - <%= build.less.functions %> - - <%= build.less.colors %> - - <%= build.less.tree %> - - <%= build.less.treedir %> # glob all files - - <%= build.less.env %> - - <%= build.less.visitor %> - - <%= build.less.import_visitor %> - - <%= build.less.join %> - - <%= build.less.to_css_visitor %> - - <%= build.less.extend_visitor %> - - <%= build.less.source_map_output %> - - # append browser-specific code - - <%= build.append.browser %> - - <%= build.append.amd %> - - # ================================= # Rhino build # ================================= From dce4f907b4bed8ce0cbba0d7c957780025f72b8e Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 2 Mar 2014 09:53:38 +0000 Subject: [PATCH 026/161] change gruntfile to use same whitespace as the rest of the project --- Gruntfile.js | 571 +++++++++++++++++++++++++-------------------------- 1 file changed, 285 insertions(+), 286 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4e2d8ce1e..11b2093d9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,317 +1,316 @@ 'use strict'; var fs = require('fs'); -module.exports = function(grunt) { +module.exports = function (grunt) { - // Report the elapsed execution time of tasks. - require('time-grunt')(grunt); + // Report the elapsed execution time of tasks. + require('time-grunt')(grunt); - // Project configuration. - grunt.initConfig({ + // Project configuration. + grunt.initConfig({ - // Metadata required for build. - build: grunt.file.readYAML('build/build.yml'), - pkg: grunt.file.readJSON('package.json'), - meta: { - license: '<%= _.pluck(pkg.licenses, "type").join(", ") %>', - copyright: 'Copyright (c) 2009-<%= grunt.template.today("yyyy") %>', - banner: - '/*!\n' + - ' * Less - <%= pkg.description %> v<%= pkg.version %>\n' + - ' * http://lesscss.org\n' + - ' *\n' + - ' * <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>>\n' + - ' * Licensed under the <%= meta.license %> License.\n' + - ' *\n' + - ' */\n\n' + - ' /**' + - ' * @license <%= meta.license %>\n' + - ' */\n\n' - }, + // Metadata required for build. + build: grunt.file.readYAML('build/build.yml'), + pkg: grunt.file.readJSON('package.json'), + meta: { + license: '<%= _.pluck(pkg.licenses, "type").join(", ") %>', + copyright: 'Copyright (c) 2009-<%= grunt.template.today("yyyy") %>', + banner: '/*!\n' + + ' * Less - <%= pkg.description %> v<%= pkg.version %>\n' + + ' * http://lesscss.org\n' + + ' *\n' + + ' * <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>>\n' + + ' * Licensed under the <%= meta.license %> License.\n' + + ' *\n' + + ' */\n\n' + + ' /**' + + ' * @license <%= meta.license %>\n' + + ' */\n\n' + }, - shell: { - options: {stdout: true, failOnError: true}, - test: { - command: 'node test' - }, - benchmark: { - command: 'node benchmark/less-benchmark.js' - }, - "browsertest-server": { - command: 'node node_modules/http-server/bin/http-server . -p 8088' - }, - "sourcemap-test": { - command: [ - 'node bin/lessc --source-map --source-map-map-inline test/less/import.less test/sourcemaps/import.css', - 'node bin/lessc --source-map --source-map-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css', - 'node node_modules/http-server/bin/http-server test/sourcemaps -p 8084'].join('&&') - } - }, + shell: { + options: {stdout: true, failOnError: true}, + test: { + command: 'node test' + }, + benchmark: { + command: 'node benchmark/less-benchmark.js' + }, + "browsertest-server": { + command: 'node node_modules/http-server/bin/http-server . -p 8088' + }, + "sourcemap-test": { + command: [ + 'node bin/lessc --source-map --source-map-map-inline test/less/import.less test/sourcemaps/import.css', + 'node bin/lessc --source-map --source-map-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css', + 'node node_modules/http-server/bin/http-server test/sourcemaps -p 8084'].join('&&') + } + }, - browserify: { - browser: { - src: ['./lib/less/browser.js'], - dest: 'tmp/less.js' - } - }, - concat: { - options: { - stripBanners: 'all', - banner: '<%= meta.banner %>' - }, - browsertest: { - src: '<%= browserify.browser.dest %>', - dest: 'test/browser/less.js' - }, - dist: { - src: '<%= browserify.browser.dest %>', - dest: 'dist/less.js' - }, - // Rhino - rhino: { - options: { - banner: '/* Less.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n', - footer: '' // override task-level footer + browserify: { + browser: { + src: ['./lib/less/browser.js'], + dest: 'tmp/less.js' + } }, - src: ['<%= build.rhino %>'], - dest: 'dist/less-rhino.js' - }, - // lessc for Rhino - rhinolessc: { - options: { - banner: '/* Less.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n', - footer: '' // override task-level footer + concat: { + options: { + stripBanners: 'all', + banner: '<%= meta.banner %>' + }, + browsertest: { + src: '<%= browserify.browser.dest %>', + dest: 'test/browser/less.js' + }, + dist: { + src: '<%= browserify.browser.dest %>', + dest: 'dist/less.js' + }, + // Rhino + rhino: { + options: { + banner: '/* Less.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n', + footer: '' // override task-level footer + }, + src: ['<%= build.rhino %>'], + dest: 'dist/less-rhino.js' + }, + // lessc for Rhino + rhinolessc: { + options: { + banner: '/* Less.js v<%= pkg.version %> RHINO | <%= meta.copyright %>, <%= pkg.author.name %> <<%= pkg.author.email %>> */\n\n', + footer: '' // override task-level footer + }, + src: ['<%= build.rhinolessc %>'], + dest: 'dist/lessc-rhino.js' + }, + // Generate readme + readme: { + // override task-level banner and footer + options: {process: true, banner: '', footer: ''}, + src: ['build/README.md'], + dest: 'README.md' + } }, - src: ['<%= build.rhinolessc %>'], - dest: 'dist/lessc-rhino.js' - }, - // Generate readme - readme: { - // override task-level banner and footer - options: {process: true, banner: '', footer: ''}, - src: ['build/README.md'], - dest: 'README.md' - } - }, - uglify: { - options: { - banner: '<%= meta.banner %>', - mangle: true - }, - dist: { - src: ['<%= concat.dist.dest %>'], - dest: 'dist/less.min.js' - } - }, + uglify: { + options: { + banner: '<%= meta.banner %>', + mangle: true + }, + dist: { + src: ['<%= concat.dist.dest %>'], + dest: 'dist/less.min.js' + } + }, - jshint: { - options: {jshintrc: '.jshintrc'}, - files: { - src: [ - 'Gruntfile.js', - 'lib/less/**/*.js', - 'bin/lessc' - ] - } - }, + jshint: { + options: {jshintrc: '.jshintrc'}, + files: { + src: [ + 'Gruntfile.js', + 'lib/less/**/*.js', + 'bin/lessc' + ] + } + }, - connect: { - server: { - options: { - port: 8081 - } - } - }, + connect: { + server: { + options: { + port: 8081 + } + } + }, - jasmine: { - options: { - // version: '2.0.0-rc2', - keepRunner: true, - host: 'http://localhost:8081/', - vendor: ['test/browser/common.js', 'test/browser/less.js'], - template: 'test/browser/test-runner-template.tmpl' - }, - main: { - // src is used to build list of less files to compile - src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less', '!test/less/empty.less'], - options: { - helpers: 'test/browser/runner-main-options.js', - specs: 'test/browser/runner-main-spec.js', - outfile: 'tmp/browser/test-runner-main.html' - } - }, - legacy: { - src: ['test/less/legacy/*.less'], - options: { - helpers: 'test/browser/runner-legacy-options.js', - specs: 'test/browser/runner-legacy-spec.js', - outfile: 'tmp/browser/test-runner-legacy.html' - } - }, - errors: { - src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], - options: { - timeout: 20000, - helpers: 'test/browser/runner-errors-options.js', - specs: 'test/browser/runner-errors-spec.js', - outfile: 'tmp/browser/test-runner-errors.html' - } - }, - noJsErrors: { - src: ['test/less/no-js-errors/*.less'], - options: { - helpers: 'test/browser/runner-no-js-errors-options.js', - specs: 'test/browser/runner-no-js-errors-spec.js', - outfile: 'tmp/browser/test-runner-no-js-errors.html' - } - }, - browser: { - src: ['test/browser/less/*.less'], - options: { - helpers: 'test/browser/runner-browser-options.js', - specs: 'test/browser/runner-browser-spec.js', - outfile: 'tmp/browser/test-runner-browser.html' - } - }, - relativeUrls: { - src: ['test/browser/less/relative-urls/*.less'], - options: { - helpers: 'test/browser/runner-relative-urls-options.js', - specs: 'test/browser/runner-relative-urls-spec.js', - outfile: 'tmp/browser/test-runner-relative-urls.html' - } - }, - rootpath: { - src: ['test/browser/less/rootpath/*.less'], - options: { - helpers: 'test/browser/runner-rootpath-options.js', - specs: 'test/browser/runner-rootpath-spec.js', - outfile: 'tmp/browser/test-runner-rootpath.html' - } - }, - rootpathRelative: { - src: ['test/browser/less/rootpath-relative/*.less'], - options: { - helpers: 'test/browser/runner-rootpath-relative-options.js', - specs: 'test/browser/runner-rootpath-relative-spec.js', - outfile: 'tmp/browser/test-runner-rootpath-relative.html' - } - }, - production: { - src: ['test/browser/less/production/*.less'], - options: { - helpers: 'test/browser/runner-production-options.js', - specs: 'test/browser/runner-production-spec.js', - outfile: 'tmp/browser/test-runner-production.html' - } - }, - modifyVars: { - src: ['test/browser/less/modify-vars/*.less'], - options: { - helpers: 'test/browser/runner-modify-vars-options.js', - specs: 'test/browser/runner-modify-vars-spec.js', - outfile: 'tmp/browser/test-runner-modify-vars.html' - } - }, - globalVars: { - src: ['test/browser/less/global-vars/*.less'], - options: { - helpers: 'test/browser/runner-global-vars-options.js', - specs: 'test/browser/runner-global-vars-spec.js', - outfile: 'tmp/browser/test-runner-global-vars.html' - } - }, - postProcessor: { - src: ['test/browser/less/postProcessor/*.less'], - options: { - helpers: 'test/browser/runner-postProcessor-options.js', - specs: 'test/browser/runner-postProcessor.js', - outfile: 'tmp/browser/test-postProcessor.html' - } - } - }, + jasmine: { + options: { + // version: '2.0.0-rc2', + keepRunner: true, + host: 'http://localhost:8081/', + vendor: ['test/browser/common.js', 'test/browser/less.js'], + template: 'test/browser/test-runner-template.tmpl' + }, + main: { + // src is used to build list of less files to compile + src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less', '!test/less/empty.less'], + options: { + helpers: 'test/browser/runner-main-options.js', + specs: 'test/browser/runner-main-spec.js', + outfile: 'tmp/browser/test-runner-main.html' + } + }, + legacy: { + src: ['test/less/legacy/*.less'], + options: { + helpers: 'test/browser/runner-legacy-options.js', + specs: 'test/browser/runner-legacy-spec.js', + outfile: 'tmp/browser/test-runner-legacy.html' + } + }, + errors: { + src: ['test/less/errors/*.less', '!test/less/errors/javascript-error.less'], + options: { + timeout: 20000, + helpers: 'test/browser/runner-errors-options.js', + specs: 'test/browser/runner-errors-spec.js', + outfile: 'tmp/browser/test-runner-errors.html' + } + }, + noJsErrors: { + src: ['test/less/no-js-errors/*.less'], + options: { + helpers: 'test/browser/runner-no-js-errors-options.js', + specs: 'test/browser/runner-no-js-errors-spec.js', + outfile: 'tmp/browser/test-runner-no-js-errors.html' + } + }, + browser: { + src: ['test/browser/less/*.less'], + options: { + helpers: 'test/browser/runner-browser-options.js', + specs: 'test/browser/runner-browser-spec.js', + outfile: 'tmp/browser/test-runner-browser.html' + } + }, + relativeUrls: { + src: ['test/browser/less/relative-urls/*.less'], + options: { + helpers: 'test/browser/runner-relative-urls-options.js', + specs: 'test/browser/runner-relative-urls-spec.js', + outfile: 'tmp/browser/test-runner-relative-urls.html' + } + }, + rootpath: { + src: ['test/browser/less/rootpath/*.less'], + options: { + helpers: 'test/browser/runner-rootpath-options.js', + specs: 'test/browser/runner-rootpath-spec.js', + outfile: 'tmp/browser/test-runner-rootpath.html' + } + }, + rootpathRelative: { + src: ['test/browser/less/rootpath-relative/*.less'], + options: { + helpers: 'test/browser/runner-rootpath-relative-options.js', + specs: 'test/browser/runner-rootpath-relative-spec.js', + outfile: 'tmp/browser/test-runner-rootpath-relative.html' + } + }, + production: { + src: ['test/browser/less/production/*.less'], + options: { + helpers: 'test/browser/runner-production-options.js', + specs: 'test/browser/runner-production-spec.js', + outfile: 'tmp/browser/test-runner-production.html' + } + }, + modifyVars: { + src: ['test/browser/less/modify-vars/*.less'], + options: { + helpers: 'test/browser/runner-modify-vars-options.js', + specs: 'test/browser/runner-modify-vars-spec.js', + outfile: 'tmp/browser/test-runner-modify-vars.html' + } + }, + globalVars: { + src: ['test/browser/less/global-vars/*.less'], + options: { + helpers: 'test/browser/runner-global-vars-options.js', + specs: 'test/browser/runner-global-vars-spec.js', + outfile: 'tmp/browser/test-runner-global-vars.html' + } + }, + postProcessor: { + src: ['test/browser/less/postProcessor/*.less'], + options: { + helpers: 'test/browser/runner-postProcessor-options.js', + specs: 'test/browser/runner-postProcessor.js', + outfile: 'tmp/browser/test-postProcessor.html' + } + } + }, - // Clean the version of less built for the tests - clean: { - test: ['test/browser/less.js', 'tmp'], - "sourcemap-test": ['test/sourcemaps/*.css', 'test/sourcemaps/*.map'] - } - }); + // Clean the version of less built for the tests + clean: { + test: ['test/browser/less.js', 'tmp'], + "sourcemap-test": ['test/sourcemaps/*.css', 'test/sourcemaps/*.map'] + } + }); - // Load these plugins to provide the necessary tasks - require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); + // Load these plugins to provide the necessary tasks + require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks); - // Actually load this plugin's task(s). - grunt.loadTasks('build/tasks'); + // Actually load this plugin's task(s). + grunt.loadTasks('build/tasks'); - // by default, run tests - grunt.registerTask('default', [ - 'test' - ]); + // by default, run tests + grunt.registerTask('default', [ + 'test' + ]); - grunt.registerTask('updateBowerJson', function() { - var bowerJson = require('./bower.json'); - bowerJson.version = grunt.config('pkg.version'); - fs.writeFileSync('./bower.json', JSON.stringify(bowerJson,null,2)); - }); + grunt.registerTask('updateBowerJson', function () { + var bowerJson = require('./bower.json'); + bowerJson.version = grunt.config('pkg.version'); + fs.writeFileSync('./bower.json', JSON.stringify(bowerJson, null, 2)); + }); - // Release - grunt.registerTask('dist', [ - 'browserify:browser', - 'concat:dist', - 'uglify:dist', - 'updateBowerJson' - ]); + // Release + grunt.registerTask('dist', [ + 'browserify:browser', + 'concat:dist', + 'uglify:dist', + 'updateBowerJson' + ]); - // Release Rhino Version - grunt.registerTask('rhino', [ - 'browserify:rhino', - 'concat:rhino', - 'concat:rhinolessc' - ]); + // Release Rhino Version + grunt.registerTask('rhino', [ + 'browserify:rhino', + 'concat:rhino', + 'concat:rhinolessc' + ]); - // Create the browser version of less.js - grunt.registerTask('browsertest-lessjs', [ - 'browserify:browser', - 'concat:browsertest' - ]); + // Create the browser version of less.js + grunt.registerTask('browsertest-lessjs', [ + 'browserify:browser', + 'concat:browsertest' + ]); // Run all browser tests - grunt.registerTask('browsertest', [ - 'browsertest-lessjs', - 'connect', - 'jasmine' - ]); + grunt.registerTask('browsertest', [ + 'browsertest-lessjs', + 'connect', + 'jasmine' + ]); - // setup a web server to run the browser tests in a browser rather than phantom - grunt.registerTask('browsertest-server', [ - 'browsertest-less', - 'shell:browsertest-server' - ]); + // setup a web server to run the browser tests in a browser rather than phantom + grunt.registerTask('browsertest-server', [ + 'browsertest-less', + 'shell:browsertest-server' + ]); - // Run all tests - grunt.registerTask('test', [ - 'clean', - 'jshint', - 'shell:test', - 'browsertest' - ]); + // Run all tests + grunt.registerTask('test', [ + 'clean', + 'jshint', + 'shell:test', + 'browsertest' + ]); - // generate a good test environment for testing sourcemaps - grunt.registerTask('sourcemap-test', [ - 'clean:sourcemap-test', - 'shell:sourcemap-test' - ]); + // generate a good test environment for testing sourcemaps + grunt.registerTask('sourcemap-test', [ + 'clean:sourcemap-test', + 'shell:sourcemap-test' + ]); - // Run benchmark - grunt.registerTask('benchmark', [ - 'shell:benchmark' - ]); + // Run benchmark + grunt.registerTask('benchmark', [ + 'shell:benchmark' + ]); - // Readme. - grunt.registerTask('readme', [ - 'concat:readme' - ]); + // Readme. + grunt.registerTask('readme', [ + 'concat:readme' + ]); }; From e5b0fb6967c16b47f72cd820928619ad543da238 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 2 Mar 2014 10:19:05 +0000 Subject: [PATCH 027/161] move the visitors to return themselves instead of assuming where they live --- lib/less/browser.js | 2 +- lib/less/colors.js | 151 ------------------ lib/less/data/colors.js | 149 +++++++++++++++++ lib/less/{environments => environment}/api.js | 0 .../{environments => environment}/browser.js | 0 .../{environments => environment}/node.js | 0 lib/less/extend-visitor.js | 11 +- lib/less/import-visitor.js | 6 +- lib/less/index.js | 2 +- lib/less/join-selector-visitor.js | 11 +- lib/less/non-node-index.js | 19 ++- lib/less/parser.js | 2 +- lib/less/source-map-output.js | 13 +- lib/less/to-css-visitor.js | 6 +- lib/less/tree.js | 2 +- lib/less/tree/color.js | 6 +- lib/less/visitor.js | 6 +- 17 files changed, 196 insertions(+), 190 deletions(-) delete mode 100644 lib/less/colors.js create mode 100644 lib/less/data/colors.js rename lib/less/{environments => environment}/api.js (100%) rename lib/less/{environments => environment}/browser.js (100%) rename lib/less/{environments => environment}/node.js (100%) diff --git a/lib/less/browser.js b/lib/less/browser.js index 6ba6fa75d..a335f19fa 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -21,7 +21,7 @@ function log(str, level) { var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); -less.environment = require("./environments/browser.js")(less, isFileProtocol, log, logLevel); +less.environment = require("./environment/browser.js")(less, isFileProtocol, log, logLevel); window.less = less; less.env = options.env || (location.hostname == '127.0.0.1' || diff --git a/lib/less/colors.js b/lib/less/colors.js deleted file mode 100644 index dd213f2cd..000000000 --- a/lib/less/colors.js +++ /dev/null @@ -1,151 +0,0 @@ -module.exports = function (tree) { - tree.colors = { - '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' - }; -}; diff --git a/lib/less/data/colors.js b/lib/less/data/colors.js new file mode 100644 index 000000000..30476a998 --- /dev/null +++ b/lib/less/data/colors.js @@ -0,0 +1,149 @@ +module.exports = { + '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' +}; \ No newline at end of file diff --git a/lib/less/environments/api.js b/lib/less/environment/api.js similarity index 100% rename from lib/less/environments/api.js rename to lib/less/environment/api.js diff --git a/lib/less/environments/browser.js b/lib/less/environment/browser.js similarity index 100% rename from lib/less/environments/browser.js rename to lib/less/environment/browser.js diff --git a/lib/less/environments/node.js b/lib/less/environment/node.js similarity index 100% rename from lib/less/environments/node.js rename to lib/less/environment/node.js diff --git a/lib/less/extend-visitor.js b/lib/less/extend-visitor.js index 211e6adde..0fd22a44a 100644 --- a/lib/less/extend-visitor.js +++ b/lib/less/extend-visitor.js @@ -1,13 +1,13 @@ module.exports = function (tree) { /*jshint loopfunc:true */ - tree.extendFinderVisitor = function() { + var extendFinderVisitor = function() { this._visitor = new tree.visitor(this); this.contexts = []; this.allExtendsStack = [[]]; }; - tree.extendFinderVisitor.prototype = { + extendFinderVisitor.prototype = { run: function (root) { root = this._visitor.visit(root); root.allExtends = this.allExtendsStack[0]; @@ -85,13 +85,13 @@ module.exports = function (tree) { } }; - tree.processExtendsVisitor = function() { + var processExtendsVisitor = function() { this._visitor = new tree.visitor(this); }; - tree.processExtendsVisitor.prototype = { + processExtendsVisitor.prototype = { run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); + var extendFinder = new extendFinderVisitor(); extendFinder.run(root); if (!extendFinder.foundExtends) { return root; } root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); @@ -413,4 +413,5 @@ module.exports = function (tree) { } }; + return processExtendsVisitor; }; diff --git a/lib/less/import-visitor.js b/lib/less/import-visitor.js index b94f58d13..3da88b534 100644 --- a/lib/less/import-visitor.js +++ b/lib/less/import-visitor.js @@ -1,5 +1,5 @@ module.exports = function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { + var importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { this._visitor = new tree.visitor(this); this._importer = importer; this._finish = finish; @@ -16,7 +16,7 @@ module.exports = function (tree) { } }; - tree.importVisitor.prototype = { + importVisitor.prototype = { isReplacing: true, run: function (root) { var error; @@ -138,5 +138,5 @@ module.exports = function (tree) { this.env.frames.shift(); } }; - + return importVisitor; }; diff --git a/lib/less/index.js b/lib/less/index.js index ea47acbf8..f4730de63 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -90,6 +90,6 @@ less.writeError = function (ctx, options) { console.error(less.formatError(ctx, options)); }; -less.environment = require("./environments/node"); +less.environment = require("./environment/node"); module.exports = less; diff --git a/lib/less/join-selector-visitor.js b/lib/less/join-selector-visitor.js index 13b3a590e..bc511d98e 100644 --- a/lib/less/join-selector-visitor.js +++ b/lib/less/join-selector-visitor.js @@ -1,10 +1,10 @@ -module.exports = function (tree) { - tree.joinSelectorVisitor = function() { +module.exports = function (visitor) { + var joinSelectorVisitor = function() { this.contexts = [[]]; - this._visitor = new tree.visitor(this); + this._visitor = new visitor(this); }; - tree.joinSelectorVisitor.prototype = { + joinSelectorVisitor.prototype = { run: function (root) { return this._visitor.visit(root); }, @@ -40,5 +40,6 @@ module.exports = function (tree) { mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); } }; - + + return joinSelectorVisitor; }; \ No newline at end of file diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 5cd0ad163..e3426a707 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -1,5 +1,8 @@ var less = { - version: [1, 6, 3] + version: [1, 6, 3], + data: { + colors: require('./data/colors') + } }; less.tree = (require('./tree'))(less); @@ -7,12 +10,12 @@ less.Parser = (require('./parser'))(less, less.tree); less.tree.functions = (require('./functions'))(less, less.tree); require('./env')(less.tree); -require('./colors')(less.tree); -require('./visitor.js')(less.tree); -require('./import-visitor.js')(less.tree); -require('./extend-visitor.js')(less.tree); -require('./join-selector-visitor.js')(less.tree); -require('./to-css-visitor.js')(less.tree); -require('./source-map-output.js')(less, less.tree); +less.tree.visitor = require('./visitor.js')(less.tree); +less.tree.importVisitor = require('./import-visitor.js')(less.tree); +less.tree.extendVisitor = require('./extend-visitor.js')(less.tree); +less.tree.joinSelectorVisitor = require('./join-selector-visitor.js')(less.tree.visitor); +less.tree.toCSSVisitor = require('./to-css-visitor.js')(less.tree); + +less.tree.sourceMapOutput = require('./source-map-output.js')(less); module.exports = less; diff --git a/lib/less/parser.js b/lib/less/parser.js index 2afa3f406..b2ae22434 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -558,7 +558,7 @@ var Parser = function Parser(env) { var preEvalVisitors = [], visitors = [ new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), + new(tree.extendVisitor)(), new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) ], i, root = this; diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 9cdc8181b..b32bda26e 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -1,6 +1,6 @@ -module.exports = function (less, tree) { +module.exports = function (less) { - tree.sourceMapOutput = function (options) { + var sourceMapOutput = function (options) { this._css = []; this._rootNode = options.rootNode; this._writeSourceMap = options.writeSourceMap; @@ -24,7 +24,7 @@ module.exports = function (less, tree) { this._column = 0; }; - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { + sourceMapOutput.prototype.normalizeFilename = function(filename) { filename = filename.replace(/\\/g, '/'); if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { @@ -36,7 +36,7 @@ module.exports = function (less, tree) { return (this._sourceMapRootpath || "") + filename; }; - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { + sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { //ignore adding empty strings if (!chunk) { @@ -92,11 +92,11 @@ module.exports = function (less, tree) { this._css.push(chunk); }; - tree.sourceMapOutput.prototype.isEmpty = function() { + sourceMapOutput.prototype.isEmpty = function() { return this._css.length === 0; }; - tree.sourceMapOutput.prototype.toCSS = function(env) { + sourceMapOutput.prototype.toCSS = function(env) { this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); if (this._outputSourceFiles) { @@ -138,4 +138,5 @@ module.exports = function (less, tree) { return this._css.join(''); }; + return sourceMapOutput; }; diff --git a/lib/less/to-css-visitor.js b/lib/less/to-css-visitor.js index c58f11a60..ad689e443 100644 --- a/lib/less/to-css-visitor.js +++ b/lib/less/to-css-visitor.js @@ -1,10 +1,10 @@ module.exports = function (tree) { - tree.toCSSVisitor = function(env) { + var toCSSVisitor = function(env) { this._visitor = new tree.visitor(this); this._env = env; }; - tree.toCSSVisitor.prototype = { + toCSSVisitor.prototype = { isReplacing: true, run: function (root) { return this._visitor.visit(root); @@ -236,4 +236,6 @@ module.exports = function (tree) { }); } }; + + return toCSSVisitor; }; diff --git a/lib/less/tree.js b/lib/less/tree.js index fac177b48..393f71b17 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -97,7 +97,7 @@ tree.outputRuleset = function (env, output, rules) { }; tree.Alpha = require('./tree/alpha')(tree); -tree.Color = require('./tree/color')(tree); +tree.Color = require('./tree/color')(less.data, tree); tree.Directive = require('./tree/directive')(tree); tree.DetachedRuleset = require('./tree/detached-ruleset')(tree); tree.Operation = require('./tree/operation')(tree); diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 0cf128b10..ffe9dfbe3 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +module.exports = function (data, tree) { // // RGB Colors - #ff0014, #eee // @@ -165,8 +165,8 @@ Color.prototype = { Color.fromKeyword = function(keyword) { var c, key = keyword.toLowerCase(); - if (tree.colors.hasOwnProperty(key)) { - c = new(Color)(tree.colors[key].slice(1)); + if (data.colors.hasOwnProperty(key)) { + c = new(Color)(data.colors[key].slice(1)); } else if (key === "transparent") { c = new(Color)([0, 0, 0], 0); diff --git a/lib/less/visitor.js b/lib/less/visitor.js index d4696e725..d6cade478 100644 --- a/lib/less/visitor.js +++ b/lib/less/visitor.js @@ -30,7 +30,7 @@ module.exports = function (tree) { return ticker; } - tree.visitor = function(implementation) { + var visitor = function(implementation) { this._implementation = implementation; this._visitFnCache = []; @@ -40,7 +40,7 @@ module.exports = function (tree) { } }; - tree.visitor.prototype = { + visitor.prototype = { visit: function(node) { if (!node) { return node; @@ -142,5 +142,5 @@ module.exports = function (tree) { return out; } }; - + return visitor; }; From 7efbcd31cf4b86b04f4f2e59358ccb4903ed7009 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Sat, 1 Mar 2014 19:55:53 +0200 Subject: [PATCH 028/161] Trim trailing whitespace. --- CHANGELOG.md | 4 +- CONTRIBUTING.md | 16 ++++---- benchmark/benchmark.less | 18 ++++----- build.gradle | 28 ++++++------- build/build.yml | 2 +- build/rhino-modules.js | 6 +-- lib/less/functions.js | 40 +++++++++---------- lib/less/import-visitor.js | 6 +-- lib/less/index.js | 14 +++---- lib/less/parser.js | 48 +++++++++++------------ lib/less/rhino.js | 10 ++--- lib/less/source-map-output.js | 2 +- lib/less/tree/anonymous.js | 8 ++-- lib/less/tree/call.js | 2 +- lib/less/tree/color.js | 4 +- lib/less/tree/dimension.js | 4 +- lib/less/tree/import.js | 4 +- lib/less/tree/media.js | 10 ++--- lib/less/tree/mixin.js | 26 ++++++------ lib/less/tree/quoted.js | 6 +-- lib/less/tree/rule.js | 4 +- lib/less/tree/ruleset.js | 26 ++++++------ lib/less/tree/selector.js | 6 +-- lib/less/tree/url.js | 2 +- lib/less/tree/variable.js | 6 +-- lib/source-map/source-map-footer.js | 4 +- lib/source-map/source-map-header.js | 2 +- test/browser/jasmine.js | 16 ++++---- test/index.js | 6 +-- test/less/colors.less | 2 +- test/less/comments.less | 10 ++--- test/less/css-escapes.less | 20 +++++----- test/less/css.less | 2 +- test/less/errors/import-missing.less | 2 +- test/less/extend-selector.less | 2 +- test/less/extract-and-length.less | 12 +++--- test/less/functions.less | 6 +-- test/less/globalVars/simple.json | 2 +- test/less/import/imports/font.less | 6 +-- test/less/media.less | 4 +- test/less/merge.less | 2 +- test/less/mixins-closure.less | 8 ++-- test/less/mixins-guards-default-func.less | 6 +-- test/less/mixins-guards.less | 22 +++++------ test/less/mixins-important.less | 2 +- test/less/mixins-named-args.less | 4 +- test/less/mixins-nested.less | 4 +- test/less/mixins.less | 2 +- test/less/property-name-interp.less | 4 +- test/less/scope.less | 2 +- test/less/selectors.less | 8 ++-- test/less/sourcemaps/basic.json | 2 +- test/less/variables.less | 20 +++++----- test/less/whitespace.less | 10 ++--- 54 files changed, 247 insertions(+), 247 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd7602731..3cf3ff432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,7 +135,7 @@ - fix passing of strict maths option # 1.4.0 Beta 4 - + 2013-05-04 - change strictMaths to strictMath. Enable this with --strict-math=on in lessc and strictMath:true in JavaScript. @@ -168,7 +168,7 @@ - significant bug fixes to our debug options - other parameters can be used as defaults in mixins e.g. .a(@a, @b:@a) - an error is shown if properties are used outside of a ruleset - - added extract function which picks a value out of a list, e.g. extract(12 13 14, 3) => 14 + - added extract function which picks a value out of a list, e.g. extract(12 13 14, 3) => 14 - added luma, hsvhue, hsvsaturation, hsvvalue functions - added pow, pi, mod, tan, sin, cos, atan, asin, acos and sqrt math functions - added convert function, e.g. convert(1rad, deg) => value in degrees diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6508bb1ef..b838412a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,21 @@ # Contributing to Less.js -> We welcome feature requests and bug reports. Please read these guidelines before submitting one. +> We welcome feature requests and bug reports. Please read these guidelines before submitting one. **Words that begin with the at sign (`@`) must be wrapped in backticks!** . As a courtesy to avoid sending notifications to any user that might have the `@username` being referenced, please remember that GitHub usernames also start with the at sign. If you don't wrap them in backticks, users will get unintended notifications from you. -GitHub has other great markdown features as well, [go here to learn more about them](https://help.github.com/articles/github-flavored-markdown). +GitHub has other great markdown features as well, [go here to learn more about them](https://help.github.com/articles/github-flavored-markdown). ## Reporting Issues -We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems that we can fix within the Less.js core. Please read the following guidelines before opening any issue. +We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems that we can fix within the Less.js core. Please read the following guidelines before opening any issue. 1. **Search for existing issues.** We get a lot of duplicate issues, and you'd help us out a lot by first checking if someone else has reported the same issue. Moreover, the issue may have already been resolved with a fix available. 2. **Create an isolated and reproducible test case.** Be sure the problem exists in Less.js's code with [reduced test cases](http://css-tricks.com/reduced-test-cases/) that should be included in each bug report. -3. **Test with the latest version**. We get a lot of issues that could be resolved by updating your version of Less.js. -3. **Include a live example.** Please use [less2css.org](http://less2css.org/) for sharing your isolated test cases. +3. **Test with the latest version**. We get a lot of issues that could be resolved by updating your version of Less.js. +3. **Include a live example.** Please use [less2css.org](http://less2css.org/) for sharing your isolated test cases. 4. **Share as much information as possible.** Include operating system and version. Describe how you use Less. If you use it in the browser, please include browser and version, and the version of Less.js you're using. Let us know if you're using the command line (`lessc`) or an external tool. And try to include steps to reproduce the bug. 5. If you have a solution or suggestion for how to fix the bug you're reporting, please include it, or make a pull request - don't assume the maintainers know how to fix it just because you do. @@ -32,15 +32,15 @@ Please report documentation issues in [the documentation project](https://github _Pull requests are encouraged!_ -* Start by adding a feature request to get feedback and see how your idea is received. +* Start by adding a feature request to get feedback and see how your idea is received. * If your pull request solves an existing issue, but it's different in some way, _please create a new issue_ and make sure to discuss it with the core contributors. Otherwise you risk your hard work being rejected. * Do not change the **./dist/** folder, we do this when releasing -* _Please add tests_ for your work. Tests are invoked using `grunt test` command. It will run both node.js tests and browser ([PhantomJS](http://phantomjs.org/)) tests. +* _Please add tests_ for your work. Tests are invoked using `grunt test` command. It will run both node.js tests and browser ([PhantomJS](http://phantomjs.org/)) tests. ### Coding Standards * Always use spaces, never tabs -* End lines in semi-colons. +* End lines in semi-colons. * Loosely aim towards jsHint standards diff --git a/benchmark/benchmark.less b/benchmark/benchmark.less index c9c0f6679..6053420b1 100644 --- a/benchmark/benchmark.less +++ b/benchmark/benchmark.less @@ -2080,9 +2080,9 @@ div.panel { */ -/* +/* * Comment Test - * + * * - cloudhead (http://cloudhead.net) * */ @@ -2105,7 +2105,7 @@ div.panel { /* @group Variables ------------------- */ #comments /* boo */ { - /**/ // An empty comment + /**/ // An empty comment color: red; /* A C-style comment */ background-color: orange; // A little comment font-size: 12px; @@ -2844,9 +2844,9 @@ td, input { */ -/* +/* * Comment Test - * + * * - cloudhead (http://cloudhead.net) * */ @@ -2869,7 +2869,7 @@ td, input { /* @group Variables ------------------- */ #comments /* boo */ { - /**/ // An empty comment + /**/ // An empty comment color: red; /* A C-style comment */ background-color: orange; // A little comment font-size: 12px; @@ -3608,9 +3608,9 @@ td, input { */ -/* +/* * Comment Test - * + * * - cloudhead (http://cloudhead.net) * */ @@ -3633,7 +3633,7 @@ td, input { /* @group Variables ------------------- */ #comments /* boo */ { - /**/ // An empty comment + /**/ // An empty comment color: red; /* A C-style comment */ background-color: orange; // A little comment font-size: 12px; diff --git a/build.gradle b/build.gradle index 7042b929a..b626b5dd3 100644 --- a/build.gradle +++ b/build.gradle @@ -123,13 +123,13 @@ task clean << { } class SourceMapRhinoTest extends RhinoTest { - + // helper to get the output map file def getOutputMap(lessFile) { def outFile = project.file(lessFile.path.replace('test/less', project.testOut).replace('.less', '.css')) return project.file(outFile.path + ".map"); } - + // callback to add SourceMap options to the options list def postProcessOptions(options, lessFile) { def outFile = getOutputMap(lessFile) @@ -141,14 +141,14 @@ class SourceMapRhinoTest extends RhinoTest { options } - + // Callback to validate output def handleResult(exec, out, lessFile) { def actualFile = getOutputMap(lessFile) - def expectedFile = project.file(projectDir + fs + "test" + fs + testDir + fs + lessFile.name.replace(".less", ".json")) + def expectedFile = project.file(projectDir + fs + "test" + fs + testDir + fs + lessFile.name.replace(".less", ".json")) assert actualFile.text == expectedFile.text } - + } class DebugRhinoTest extends RhinoTest { @@ -159,11 +159,11 @@ class DebugRhinoTest extends RhinoTest { def globalReplacements(input, directory) { def pDirectory = toPlatformFs(directory) - def p = lessRootDir + fs + pDirectory + def p = lessRootDir + fs + pDirectory def pathimport = p + toPlatformFs("import/") def pathesc = escapeIt(p) def pathimportesc = escapeIt(pathimport) - + def result = input.replace("{path}", p).replace("{pathesc}", pathesc).replace("{pathimport}", pathimport) return result.replace("{pathimportesc}", pathimportesc).replace("\r\n", "\n") } @@ -182,16 +182,16 @@ class RhinoTest extends DefaultTask { def fs = File.separator; def projectDir = toUpperCaseDriveLetter(System.getProperty("user.dir")); def lessRootDir = projectDir + fs + "test" + fs + "less" - + def toUpperCaseDriveLetter(path) { if (path.charAt(1)==':' && path.charAt(2)=='\\') { return path.substring(0,1).toUpperCase() + path.substring(1); - } + } return path; } def toPlatformFs(path) { - return path.replace('\\', fs).replace('/', fs); + return path.replace('\\', fs).replace('/', fs); } def expectedCssPath(lessFilePath) { @@ -216,12 +216,12 @@ class RhinoTest extends DefaultTask { return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm'; } - + // Callback for subclasses to make any changes to the options def postProcessOptions(options, lessFile) { options } - + // Callback to validate output def handleResult(exec, out, lessFile) { def actual = out.toString().trim() @@ -273,7 +273,7 @@ class RhinoTest extends DefaultTask { standardOutput = out ignoreExitValue = true } - println "rhinoTestSrc: ${project.rhinoTestSrc}" + println "rhinoTestSrc: ${project.rhinoTestSrc}" try { def exec = project.javaexec(execOptions) handleResult(exec, out, lessFile) @@ -333,7 +333,7 @@ class GruntTask extends Exec { private String gruntExecutable = Os.isFamily(Os.FAMILY_WINDOWS) ? "grunt.cmd" : "grunt" private String switches = "--no-color" - String gruntArgs = "" + String gruntArgs = "" public GruntTask() { super() diff --git a/build/build.yml b/build/build.yml index b5a2234a8..fca8a3548 100644 --- a/build/build.yml +++ b/build/build.yml @@ -150,7 +150,7 @@ tree: - <%= build.lib %>/tree/url.js - <%= build.lib %>/tree/value.js - <%= build.lib %>/tree/variable.js - + # ================================= # source-map build # ================================= diff --git a/build/rhino-modules.js b/build/rhino-modules.js index 260a866a2..78829d2fb 100644 --- a/build/rhino-modules.js +++ b/build/rhino-modules.js @@ -55,9 +55,9 @@ } else if (part === '' && result.length > 0) { // skip } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } + if (part.slice(-1)==='\\' || part.slice(-1)==='/') { + part = part.slice(0, -1); + } result.push(part); } } diff --git a/lib/less/functions.js b/lib/less/functions.js index 7f048a477..0a30e1062 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -302,7 +302,7 @@ tree.functions = { continue; } currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); + unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; @@ -392,12 +392,12 @@ tree.functions = { }, shade: function(color, amount) { return this.mix(this.rgb(0, 0, 0), color, amount); - }, + }, extract: function(values, index) { - index = index.value - 1; // (1-based index) + index = index.value - 1; // (1-based index) // handle non-array values as an array of length 1 // return 'undefined' if index is invalid - return Array.isArray(values.value) + return Array.isArray(values.value) ? values.value[index] : Array(values)[index]; }, length: function(values) { @@ -583,15 +583,15 @@ tree._mime = { var mathFunctions = { // name, unit - ceil: null, - floor: null, - sqrt: null, + ceil: null, + floor: null, + sqrt: null, abs: null, - tan: "", - sin: "", + tan: "", + sin: "", cos: "", - atan: "rad", - asin: "rad", + atan: "rad", + asin: "rad", acos: "rad" }; @@ -616,19 +616,19 @@ function colorBlend(mode, color1, color2) { var ab = color1.alpha, cb, // backdrop as = color2.alpha, cs, // source ar, cr, r = []; // result - + ar = as + ab * (1 - as); for (var i = 0; i < 3; i++) { cb = color1.rgb[i] / 255; cs = color2.rgb[i] / 255; cr = mode(cb, cs); if (ar) { - cr = (as * cs + ab * (cb + cr = (as * cs + ab * (cb - as * (cb + cs - cr))) / ar; } r[i] = cr * 255; } - + return new(tree.Color)(r, ar); } @@ -638,7 +638,7 @@ var colorBlendMode = { }, screen: function(cb, cs) { return cb + cs - cb * cs; - }, + }, overlay: function(cb, cs) { cb *= 2; return (cb <= 1) @@ -651,7 +651,7 @@ var colorBlendMode = { e = 1; d = (cb > 0.25) ? Math.sqrt(cb) : ((16 * cb - 12) * cb + 4) * cb; - } + } return cb - (1 - 2 * cs) * e * (d - cb); }, hardlight: function(cb, cs) { @@ -698,25 +698,25 @@ tree.defaultFunc = { function initFunctions() { var f, tf = tree.functions; - + // math for (f in mathFunctions) { if (mathFunctions.hasOwnProperty(f)) { tf[f] = _math.bind(null, Math[f], mathFunctions[f]); } } - + // color blending for (f in colorBlendMode) { if (colorBlendMode.hasOwnProperty(f)) { tf[f] = colorBlend.bind(null, colorBlendMode[f]); } } - + // default f = tree.defaultFunc; tf["default"] = f.eval.bind(f); - + } initFunctions(); function hsla(color) { diff --git a/lib/less/import-visitor.js b/lib/less/import-visitor.js index 7d6933721..a75f17fb2 100644 --- a/lib/less/import-visitor.js +++ b/lib/less/import-visitor.js @@ -38,7 +38,7 @@ var importVisitor = this, evaldImportNode, inlineCSS = importNode.options.inline; - + if (!importNode.css || inlineCSS) { try { @@ -63,7 +63,7 @@ this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - if (!env.importMultiple) { + if (!env.importMultiple) { if (importedAtRoot) { importNode.skip = true; } else { @@ -73,7 +73,7 @@ } importVisitor.onceFileDetectionMap[fullPath] = true; return false; - }; + }; } } diff --git a/lib/less/index.js b/lib/less/index.js index 348a5d8f1..5d5e01d30 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -24,7 +24,7 @@ var less = { var css; try { css = root && root.toCSS && root.toCSS(options); - } + } catch (err) { callback(err); return; } callback(null, css); }); @@ -34,7 +34,7 @@ var less = { process.nextTick(function () { parser.parse(input, function (e, root) { if (e) { return ee.emit('error', e); } - try { ee.emit('success', root.toCSS(options)); } + try { ee.emit('success', root.toCSS(options)); } catch (err) { ee.emit('error', err); } }); }); @@ -143,13 +143,13 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { function handleDataAndCallCallback(data) { var j = file.lastIndexOf('/'); - // Pass on an updated rootpath if path of imported file is relative and file + // Pass on an updated rootpath if path of imported file is relative and file // is in a (sub|sup) directory - // - // Examples: + // + // Examples: // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', // then rootpath should become 'less/module/nav/' - // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { var relativeSubDirectory = file.slice(0, j+1); @@ -160,7 +160,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { callback(null, data, pathname, newFileInfo); } - + var isUrl = isUrlRe.test( file ); if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) { if (request === undefined) { diff --git a/lib/less/parser.js b/lib/less/parser.js index 9a1bf7e59..1471a1a28 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -405,8 +405,8 @@ less.Parser = function Parser(env) { switch (cc) { case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; + parenLevel++; + lastOpeningParen = parserCurrentIndex; continue; case 41: // ) if (--parenLevel < 0) { @@ -417,8 +417,8 @@ less.Parser = function Parser(env) { if (!parenLevel) { emitChunk(); } continue; case 123: // { - level++; - lastOpening = parserCurrentIndex; + level++; + lastOpening = parserCurrentIndex; continue; case 125: // } if (--level < 0) { @@ -517,7 +517,7 @@ less.Parser = function Parser(env) { var evaldRoot, css, evalEnv = new tree.evalEnv(options); - + // // Allows setting variables with a hash, so: // @@ -1038,8 +1038,8 @@ less.Parser = function Parser(env) { rulesetCall: function () { var name; - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); + if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { + return new tree.RulesetCall(name[1]); } }, @@ -1066,7 +1066,7 @@ less.Parser = function Parser(env) { if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } } while($char(",")); - + expect(/^\)/); if (isRule) { @@ -1082,7 +1082,7 @@ less.Parser = function Parser(env) { extendRule: function() { return this.extend(true); }, - + // // Mixins // @@ -1288,7 +1288,7 @@ less.Parser = function Parser(env) { variadic = argInfo.variadic; // .mixincall("@{a}"); - // looks a bit like a mixin definition.. + // looks a bit like a mixin definition.. // also // .mixincall(@a: {rule: set;}); // so we have to be nice and restore @@ -1297,7 +1297,7 @@ less.Parser = function Parser(env) { restore(); return; } - + parsers.comments(); if ($re(/^when/)) { // Guard @@ -1404,7 +1404,7 @@ less.Parser = function Parser(env) { // combinator: function () { var c = input.charAt(i); - + if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { i++; if (input.charAt(i) === '^') { @@ -1497,7 +1497,7 @@ less.Parser = function Parser(env) { } return block; }, - + detachedRuleset: function() { var blockRuleset = this.blockRuleset(); if (blockRuleset) { @@ -1510,7 +1510,7 @@ less.Parser = function Parser(env) { // ruleset: function () { var selectors, s, rules, debugInfo; - + save(); if (env.dumpLineNumbers) { @@ -1557,20 +1557,20 @@ less.Parser = function Parser(env) { name = this.variable() || this.ruleProperty(); if (name) { isVariable = typeof name === "string"; - + if (isVariable) { value = this.detachedRuleset(); } - + if (!value) { // prefer to try to parse first if its a variable or we are compressing // but always fallback on the other one value = !tryAnonymous && (env.compress || isVariable) ? (this.value() || this.anonymousValue()) : (this.anonymousValue() || this.value()); - + important = this.important(); - + // 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 @@ -1751,7 +1751,7 @@ less.Parser = function Parser(env) { save(); name = $re(/^@[a-z-]+/); - + if (!name) { return; } nonVendorSpecificName = name; @@ -1824,7 +1824,7 @@ less.Parser = function Parser(env) { if (rules || (!hasBlock && value && $char(';'))) { forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, + return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); } @@ -1911,7 +1911,7 @@ less.Parser = function Parser(env) { if (!a) { break; } - + m.parensInOp = true; a.parensInOp = true; operation = new(tree.Operation)(op, [operation || m, a], isSpaced); @@ -2018,7 +2018,7 @@ less.Parser = function Parser(env) { }, ruleProperty: function () { var c = current, name = [], index = [], length = 0, s, k; - + function match(re) { var a = re.exec(c); if (a) { @@ -2032,7 +2032,7 @@ less.Parser = function Parser(env) { match(/^(\*?)/); while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, + // at last, we have the complete match now. move forward, // convert name particles to tree objects and return: skipWhitespace(length); if (name[0] === '') { @@ -2043,7 +2043,7 @@ less.Parser = function Parser(env) { s = name[k]; name[k] = (s.charAt(0) !== '@') ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), + : new(tree.Variable)('@' + s.slice(2, -1), index[k], env.currentFileInfo); } return name; diff --git a/lib/less/rhino.js b/lib/less/rhino.js index 2e4f51c7d..892b119dd 100644 --- a/lib/less/rhino.js +++ b/lib/less/rhino.js @@ -66,9 +66,9 @@ function loadStyleSheet(sheet, callback, reload, remaining) { input = readFile(sheetName); input = input.replace(/^\xEF\xBB\xBF/, ''); - + contents[sheetName] = input; - + var parser = new less.Parser({ paths: [sheet.href.replace(/[\w\.-]+$/, '')], contents: contents @@ -190,12 +190,12 @@ function writeFile(filename, content) { args = args.filter(function (arg) { var match = arg.match(/^-I(.+)$/); - + if (match) { options.paths.push(match[1]); return false; } - + match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! else { return arg; } @@ -373,7 +373,7 @@ function writeFile(filename, content) { } else if (options.sourceMap) { options.sourceMapOutputFilename = options.sourceMap; } - + if (!name) { console.log("lessc: no inout files"); diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 9a090d4e4..37f5fda11 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -51,7 +51,7 @@ if (fileInfo) { var inputSource = this._contentsMap[fileInfo.filename]; - + // remove vars/banner added to the top of the file if (this._contentsIgnoredCharsMap[fileInfo.filename]) { // adjust the index diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 3cfae7712..bd9f32af5 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -8,21 +8,21 @@ tree.Anonymous = function (string, index, currentFileInfo, mapLines) { }; tree.Anonymous.prototype = { type: "Anonymous", - eval: function () { + eval: function () { return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); }, compare: function (x) { if (!x.toCSS) { return -1; } - + var left = this.toCSS(), right = x.toCSS(); - + if (left === right) { return 0; } - + return left < right ? -1 : 1; }, genCSS: function (env, output) { diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index aa6acb9c8..4ee524a13 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -20,7 +20,7 @@ tree.Call.prototype = { // When evaluating a function call, // we either find the function in `tree.functions` [1], // in which case we call it, passing the evaluated arguments, - // if this returns null or we cannot find the function, we + // if this returns null or we cannot find the function, we // simply print it out as it appeared originally [2]. // // The *functions.js* file contains the built-in functions. diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index eac71f7e0..38f4ff979 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -153,7 +153,7 @@ tree.Color.prototype = { if (!x.rgb) { return -1; } - + return (x.rgb[0] === this.rgb[0] && x.rgb[1] === this.rgb[1] && x.rgb[2] === this.rgb[2] && @@ -183,7 +183,7 @@ function toHex(v) { } function clamp(v, max) { - return Math.min(Math.max(v, 0), max); + return Math.min(Math.max(v, 0), max); } })(require('../tree')); diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index aecec136b..71bf98ead 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -91,7 +91,7 @@ tree.Dimension.prototype = { if (other instanceof tree.Dimension) { var a, b, aValue, bValue; - + if (this.unit.isEmpty() || other.unit.isEmpty()) { a = this; b = other; @@ -100,7 +100,7 @@ tree.Dimension.prototype = { b = other.unify(); if (a.unit.compare(b.unit) !== 0) { return -1; - } + } } aValue = a.value; bValue = b.value; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 7042f0425..27b306d44 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -97,10 +97,10 @@ tree.Import.prototype = { this.skip = this.skip(); } if (this.skip) { - return []; + return []; } } - + if (this.options.inline) { //todo needs to reference css file not import var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 095e43e88..0a95beff5 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -31,7 +31,7 @@ tree.Media.prototype = { env.mediaBlocks = []; env.mediaPath = []; } - + var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); if(this.debugInfo) { this.rules[0].debugInfo = this.debugInfo; @@ -50,14 +50,14 @@ tree.Media.prototype = { env.strictMath = false; } } - + env.mediaPath.push(media); env.mediaBlocks.push(media); - + env.frames.unshift(this.rules[0]); media.rules = [this.rules[0].eval(env)]; env.frames.shift(); - + env.mediaPath.pop(); return env.mediaPath.length === 0 ? media.evalTop(env) : @@ -66,7 +66,7 @@ tree.Media.prototype = { variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { + emptySelectors: function() { var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; sels[0].mediaEmpty = true; diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index 506561bac..4bd66e394 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -21,7 +21,7 @@ tree.mixin.Call.prototype = { eval: function (env) { var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; + defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; args = this.arguments && this.arguments.map(function (a) { return { name: a.name, value: a.value.eval(env) }; @@ -30,12 +30,12 @@ tree.mixin.Call.prototype = { for (i = 0; i < env.frames.length; i++) { if ((mixins = env.frames[i].find(this.selector)).length > 0) { isOneFound = true; - + // 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. - + for (m = 0; m < mixins.length; m++) { mixin = mixins[m]; isRecursive = false; @@ -48,11 +48,11 @@ tree.mixin.Call.prototype = { if (isRecursive) { continue; } - - if (mixin.matchArgs(args, env)) { + + if (mixin.matchArgs(args, env)) { candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { + + if (mixin.matchCondition) { for (f = 0; f < 2; f++) { defaultFunc.value(f); conditionResult[f] = mixin.matchCondition(args, env); @@ -64,16 +64,16 @@ tree.mixin.Call.prototype = { } candidates.push(candidate); - } + } } else { candidates.push(candidate); } - + match = true; } } - + defaultFunc.reset(); count = [0, 0, 0]; @@ -92,7 +92,7 @@ tree.mixin.Call.prototype = { index: this.index, filename: this.currentFileInfo.filename }; } } - + for (m = 0; m < candidates.length; m++) { candidate = candidates[m].group; if ((candidate === defNone) || (candidate === defaultResult)) { @@ -109,7 +109,7 @@ tree.mixin.Call.prototype = { } } } - + if (match) { if (!this.currentFileInfo || !this.currentFileInfo.reference) { for (i = 0; i < rules.length; i++) { @@ -242,7 +242,7 @@ tree.mixin.Definition.prototype = { throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + ' (' + argsLength + ' for ' + this.arity + ')' }; } - + frame.prependRule(new(tree.Rule)(name, val)); evaldArguments[i] = val; } diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 131bfad90..208a6427c 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -33,14 +33,14 @@ tree.Quoted.prototype = { if (!x.toCSS) { return -1; } - + var left = this.toCSS(), right = x.toCSS(); - + if (left === right) { return 0; } - + return left < right ? -1 : 1; } }; diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index 4e1ccde69..6790b3bfb 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -34,7 +34,7 @@ tree.Rule.prototype = { if (typeof name !== "string") { // expand 'primitive' name directly to get // things faster (~10% for benchmark.less): - name = (name.length === 1) + name = (name.length === 1) && (name[0] instanceof tree.Keyword) ? name[0].value : evalName(env, name); } @@ -44,7 +44,7 @@ tree.Rule.prototype = { } try { evaldValue = this.value.eval(env); - + if (!this.variable && evaldValue.type === "DetachedRuleset") { throw { message: "Rulesets cannot be evaluated on a property.", index: this.index, filename: this.currentFileInfo.filename }; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 190230045..86b61004d 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -19,14 +19,14 @@ tree.Ruleset.prototype = { } }, eval: function (env) { - var thisSelectors = this.selectors, selectors, + var thisSelectors = this.selectors, selectors, selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; if (thisSelectors && (selCnt = thisSelectors.length)) { selectors = []; defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," + type: "Syntax", + message: "it is currently only allowed in parametric mixin guards," }); for (i = 0; i < selCnt; i++) { selector = thisSelectors[i].eval(env); @@ -35,7 +35,7 @@ tree.Ruleset.prototype = { hasOnePassingSelector = true; } } - defaultFunc.reset(); + defaultFunc.reset(); } else { hasOnePassingSelector = true; } @@ -52,7 +52,7 @@ tree.Ruleset.prototype = { if(this.debugInfo) { ruleset.debugInfo = this.debugInfo; } - + if (!hasOnePassingSelector) { rules.length = 0; } @@ -124,7 +124,7 @@ tree.Ruleset.prototype = { rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; } } - + // Evaluate everything else for (i = 0; i < rsRules.length; i++) { rule = rsRules[i]; @@ -147,7 +147,7 @@ tree.Ruleset.prototype = { // Pop the stack envFrames.shift(); envSelectors.shift(); - + if (env.mediaBlocks) { for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { env.mediaBlocks[i].bubbleSelectors(selectors); @@ -387,19 +387,19 @@ tree.Ruleset.prototype = { joinSelector: function (paths, context, selector) { - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, + var i, j, k, + hasParentSelector, newSelectors, el, sel, parentSel, + newSelectorPath, afterParentJoin, newJoinedSelector, newJoinedSelectorEmpty, lastSelector, currentElements, selectorsMultiplied; - + for (i = 0; i < selector.elements.length; i++) { el = selector.elements[i]; if (el.value === '&') { hasParentSelector = true; } } - + if (!hasParentSelector) { if (context.length > 0) { for (i = 0; i < context.length; i++) { @@ -529,7 +529,7 @@ tree.Ruleset.prototype = { } } }, - + mergeElementsOnToSelectors: function(elements, selectors) { var i, sel; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index f38934e0e..6ebedf275 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -87,9 +87,9 @@ tree.Selector.prototype = { } }, isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && + return !this.mediaEmpty && + this.elements.length === 1 && + this.elements[0].value === '&' && (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); }, eval: function (env) { diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 905626856..3fd0a217c 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -29,7 +29,7 @@ tree.URL.prototype = { } val.value = rootpath + val.value; } - + val.value = ctx.normalizePath(val.value); // Add url args if enabled diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 90d862718..94795a835 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -13,14 +13,14 @@ tree.Variable.prototype = { if (name.indexOf('@@') === 0) { name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; } - + if (this.evaluating) { throw { type: 'Name', message: "Recursive variable definition for " + name, filename: this.currentFileInfo.file, index: this.index }; } - + this.evaluating = true; variable = tree.find(env.frames, function (frame) { @@ -29,7 +29,7 @@ tree.Variable.prototype = { return v.value.eval(env); } }); - if (variable) { + if (variable) { this.evaluating = false; return variable; } else { diff --git a/lib/source-map/source-map-footer.js b/lib/source-map/source-map-footer.js index bb7dcb9ac..71e1ed896 100644 --- a/lib/source-map/source-map-footer.js +++ b/lib/source-map/source-map-footer.js @@ -1,4 +1,4 @@ // footer to wrap "source-map" module - return this.sourceMap; - }(); + return this.sourceMap; + }(); })(); \ No newline at end of file diff --git a/lib/source-map/source-map-header.js b/lib/source-map/source-map-header.js index d2f3cffa7..4432b4310 100644 --- a/lib/source-map/source-map-header.js +++ b/lib/source-map/source-map-header.js @@ -1,3 +1,3 @@ // wraps the source-map code in a less module (function() { - less.modules["source-map"] = function() { + less.modules["source-map"] = function() { diff --git a/test/browser/jasmine.js b/test/browser/jasmine.js index 6b3459b91..019efdb57 100644 --- a/test/browser/jasmine.js +++ b/test/browser/jasmine.js @@ -1100,7 +1100,7 @@ jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { type: isSuite ? 'suite' : 'spec', children: [] }; - + if (isSuite) { var children = suiteOrSpec.children(); for (var i = 0; i < children.length; i++) { @@ -1326,11 +1326,11 @@ jasmine.Matchers.prototype.toBeNull = function() { * Matcher that compares the actual to NaN. */ jasmine.Matchers.prototype.toBeNaN = function() { - this.message = function() { - return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; - }; + this.message = function() { + return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; + }; - return (this.actual !== this.actual); + return (this.actual !== this.actual); }; /** @@ -1929,7 +1929,7 @@ jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { for (var property in obj) { if (!obj.hasOwnProperty(property)) continue; if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && + fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && obj.__lookupGetter__(property) !== null) : false); } }; @@ -2061,7 +2061,7 @@ jasmine.Queue.prototype.next_ = function() { while (goAgain) { goAgain = false; - + if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { var calledSynchronously = true; var completedSynchronously = false; @@ -2099,7 +2099,7 @@ jasmine.Queue.prototype.next_ = function() { if (completedSynchronously) { onComplete(); } - + } else { self.running = false; if (self.onComplete) { diff --git a/test/index.js b/test/index.js index 33e79637f..ab281101c 100644 --- a/test/index.js +++ b/test/index.js @@ -30,12 +30,12 @@ lessTester.runTestSet({strictMath: true, relativeUrls: false, rootpath: "folder lessTester.runTestSet({strictMath: true, compress: true}, "compression/"); lessTester.runTestSet({}, "legacy/"); lessTester.runTestSet({strictMath: true, strictUnits: true, sourceMap: true, globalVars: true }, "sourcemaps/", - lessTester.testSourcemap, null, null, - function(filename, type) { + lessTester.testSourcemap, null, null, + function(filename, type) { if (type === "vars") { return path.join('test/less/', filename) + '.json'; } - return path.join('test/sourcemaps', filename) + '.json'; + return path.join('test/sourcemaps', filename) + '.json'; }); lessTester.runTestSet({globalVars: true, banner: "/**\n * Test\n */\n"}, "globalVars/", null, null, null, function(name) { return path.join('test/less/', name) + '.json'; }); diff --git a/test/less/colors.less b/test/less/colors.less index 7abda4e53..061ebc8c4 100644 --- a/test/less/colors.less +++ b/test/less/colors.less @@ -37,7 +37,7 @@ .b { color: (#eee + #fff); } // #ffffff .c { color: (#aaa * 3); } // #ffffff .d { color: (#00ee00 + #009900); } // #00ff00 - .e { color: rgba(-99.9, 31.4159, 321, 0.42); } + .e { color: rgba(-99.9, 31.4159, 321, 0.42); } } #grey { diff --git a/test/less/comments.less b/test/less/comments.less index 7859911e3..e2569b670 100644 --- a/test/less/comments.less +++ b/test/less/comments.less @@ -69,11 +69,11 @@ .mixin_def_with_colors(@a: white, // in @b: 1px //put in @b - causes problems! ---> - ) // the - when (@a = white) { - .test { - color: @b; - } + ) // the + when (@a = white) { + .test { + color: @b; + } } .mixin_def_with_colors(); diff --git a/test/less/css-escapes.less b/test/less/css-escapes.less index 6a4b28306..697503a69 100644 --- a/test/less/css-escapes.less +++ b/test/less/css-escapes.less @@ -1,30 +1,30 @@ @ugly: fuchsia; .escape\|random\|char { - color: red; + color: red; } .mixin\!tUp { - font-weight: bold; + font-weight: bold; } // class="404" .\34 04 { - background: red; - - strong { - color: @ugly; - .mixin\!tUp; - } + background: red; + + strong { + color: @ugly; + .mixin\!tUp; + } } .trailingTest\+ { - color: red; + color: red; } /* This hideous test of hideousness checks for the selector "blockquote" with various permutations of hex escapes */ \62\6c\6f \63 \6B \0071 \000075o\74 e { - color: silver; + color: silver; } [ng\:cloak], diff --git a/test/less/css.less b/test/less/css.less index 766bdd4d0..0cdebae89 100644 --- a/test/less/css.less +++ b/test/less/css.less @@ -82,7 +82,7 @@ p + h1 { background: -webkit-gradient(linear, left top, left bottom, from(red), to(blue)); margin: ; .nested-multiple { - multiple-semi-colons: yes;;;;;; + multiple-semi-colons: yes;;;;;; }; filter: alpha(opacity=100); width: auto\9; diff --git a/test/less/errors/import-missing.less b/test/less/errors/import-missing.less index 5ce8e4d9e..a8046afa5 100644 --- a/test/less/errors/import-missing.less +++ b/test/less/errors/import-missing.less @@ -1,6 +1,6 @@ .a { color: green; - // tests line number for import reference is correct + // tests line number for import reference is correct } @import "file-does-not-exist.less"; \ No newline at end of file diff --git a/test/less/extend-selector.less b/test/less/extend-selector.less index c7588ee0d..1bdf6d7fd 100644 --- a/test/less/extend-selector.less +++ b/test/less/extend-selector.less @@ -37,7 +37,7 @@ div.ext5, .ext { test: 1; } -// same as +// same as // .a .c:extend(.ext all) // .b .c:extend(.ext all) // .a .c .d diff --git a/test/less/extract-and-length.less b/test/less/extract-and-length.less index d81e118f5..8b1901c8d 100644 --- a/test/less/extract-and-length.less +++ b/test/less/extract-and-length.less @@ -26,7 +26,7 @@ color-value: extract(blue, 1); rgba-value: extract(rgba(80, 160, 240, 0.67), 1); empty-value: extract(~'', 1); - + name-length: length(name); string-length: length("string"); number-length: length(12345678); @@ -48,7 +48,7 @@ } } -.mixin-args(...) { +.mixin-args(...) { &-2 { length: length(@arguments); extract: extract(@arguments, 3) ~"|" extract(@arguments, 2) ~"|" extract(@arguments, 1); @@ -90,7 +90,7 @@ extract-1: extract(@v, 2); length-2: length(extract(@v, 2)); extract-2: extract(extract(@v, 2), 2); - + &-as-args {.mixin-args(@a, @b, @c)} } @@ -111,17 +111,17 @@ @a: a b c d, 1 2 3 4; @b: 5 6 7 8, e f g h; .3D(@a, @b); - + .3D(...) { @v1: @arguments; length-1: length(@v1); extract-1: extract(@v1, 1); - + @v2: extract(@v1, 2); length-2: length(@v2); extract-2: extract(@v2, 1); - + @v3: extract(@v2, 1); length-3: length(@v3); extract-3: extract(@v3, 3); diff --git a/test/less/functions.less b/test/less/functions.less index 4b4720d99..2712ee467 100644 --- a/test/less/functions.less +++ b/test/less/functions.less @@ -67,11 +67,11 @@ format-single-quoted: %('hello %s', "single world"); format-escaped-string: %(~"hello %s", "escaped world"); eformat: e(%("rgb(%d, %d, %d)", @r, 128, 64)); - + unitless: unit(12px); unit: unit((13px + 1px), em); unitpercentage: unit(100, %); - + get-unit: get-unit(10px); get-unit-empty: get-unit(10); @@ -123,7 +123,7 @@ fade-out: fadeOut(red, 5%); // support fadeOut and fadeout fade-in: fadein(fadeout(red, 10%), 5%); - + hsv: hsv(5, 50%, 30%); hsva: hsva(3, 50%, 30%, 0.2); diff --git a/test/less/globalVars/simple.json b/test/less/globalVars/simple.json index 76a63c5a4..2bccdc51f 100644 --- a/test/less/globalVars/simple.json +++ b/test/less/globalVars/simple.json @@ -1,3 +1,3 @@ { - "my-color": "red" + "my-color": "red" } \ No newline at end of file diff --git a/test/less/import/imports/font.less b/test/less/import/imports/font.less index 5abb7e769..822279f22 100644 --- a/test/less/import/imports/font.less +++ b/test/less/import/imports/font.less @@ -1,8 +1,8 @@ @font-face { - font-family: xecret; - src: url('../assets/xecret.ttf'); + font-family: xecret; + src: url('../assets/xecret.ttf'); } #secret { - font-family: xecret, sans-serif; + font-family: xecret, sans-serif; } diff --git a/test/less/media.less b/test/less/media.less index 01a6a0209..8bd23bb56 100644 --- a/test/less/media.less +++ b/test/less/media.less @@ -126,7 +126,7 @@ body { } @page :first { size: 8.5in 11in; - @top-left { + @top-left { margin: 1cm; } @top-left-corner { @@ -197,7 +197,7 @@ body { @bpMedium: 1000px; @media (max-width: @bpMedium) { - body { + body { .bg(); background: blue; } diff --git a/test/less/merge.less b/test/less/merge.less index 5b5def35b..a902ab9bf 100644 --- a/test/less/merge.less +++ b/test/less/merge.less @@ -41,7 +41,7 @@ .fifth-transform(); } .test5 { - // Wont merge values from mixins that merked as !important, for backwards compatibility with css + // Wont merge values from mixins that merked as !important, for backwards compatibility with css .first-transform(); .second-transform() !important; } diff --git a/test/less/mixins-closure.less b/test/less/mixins-closure.less index 01251d2ad..26c8b6e90 100644 --- a/test/less/mixins-closure.less +++ b/test/less/mixins-closure.less @@ -17,10 +17,10 @@ .nested { @var: 5px; .mixin () { - width: @var; + width: @var; } .class { - @var: 10px; - .mixin; - } + @var: 10px; + .mixin; + } } diff --git a/test/less/mixins-guards-default-func.less b/test/less/mixins-guards-default-func.less index eada93814..a71f78f5c 100644 --- a/test/less/mixins-guards-default-func.less +++ b/test/less/mixins-guards-default-func.less @@ -46,7 +46,7 @@ guard-default-definition-order { guard-default-out-of-guard { .m(1) {case-1: 1} .m(@x: default()) when (default()) {default: @x} - + &-0 { case-0: default(); .m(1); @@ -153,7 +153,7 @@ guard-default-multi-4 { .m(@x) when (default()), not(default()) {always: @x} .m(@x) when (default()) and not(default()) {never: @x} .m(2) {case: 2} - + .m(1); .m(2); } @@ -186,7 +186,7 @@ guard-default-scopes { .s3(); .m(false); } - + &-1 { .s1(); .s3(); diff --git a/test/less/mixins-guards.less b/test/less/mixins-guards.less index 6a02067b3..47c3312e0 100644 --- a/test/less/mixins-guards.less +++ b/test/less/mixins-guards.less @@ -107,23 +107,23 @@ .equality-unit-test(2px); } -.colorguard(@col) when (@col = red) { content: is @col; } -.colorguard(@col) when not (blue = @col) { content: is not blue its @col; } -.colorguard(@col) {} +.colorguard(@col) when (@col = red) { content: is @col; } +.colorguard(@col) when not (blue = @col) { content: is not blue its @col; } +.colorguard(@col) {} .colorguardtest { .colorguard(red); - .colorguard(blue); - .colorguard(purple); + .colorguard(blue); + .colorguard(purple); } -.stringguard(@str) when (@str = "theme1") { content: is theme1; } -.stringguard(@str) when not ("theme2" = @str) { content: is not theme2; } -.stringguard(@str) when (~"theme1" = @str) { content: is theme1 no quotes; } -.stringguard(@str) {} +.stringguard(@str) when (@str = "theme1") { content: is theme1; } +.stringguard(@str) when not ("theme2" = @str) { content: is not theme2; } +.stringguard(@str) when (~"theme1" = @str) { content: is theme1 no quotes; } +.stringguard(@str) {} .stringguardtest { .stringguard("theme1"); - .stringguard("theme2"); - .stringguard(theme1); + .stringguard("theme2"); + .stringguard(theme1); } .mixin(...) { diff --git a/test/less/mixins-important.less b/test/less/mixins-important.less index c8cc1d5ca..5014e816a 100644 --- a/test/less/mixins-important.less +++ b/test/less/mixins-important.less @@ -2,7 +2,7 @@ border-width: @a; } .mixin (9) { - border: 9 !important; + border: 9 !important; } .mixin (@a: 0) { border: @a; diff --git a/test/less/mixins-named-args.less b/test/less/mixins-named-args.less index d79e0f473..196f2aca0 100644 --- a/test/less/mixins-named-args.less +++ b/test/less/mixins-named-args.less @@ -6,12 +6,12 @@ .mixin (@a: 1px, @b: 50%) when (@b > 75%){ text-align: center; } - + .named-arg { color: blue; .mixin(@b: 100%); } - + .class { @var: 20%; .mixin(@b: @var); diff --git a/test/less/mixins-nested.less b/test/less/mixins-nested.less index 43443de29..cc741a11d 100644 --- a/test/less/mixins-nested.less +++ b/test/less/mixins-nested.less @@ -7,9 +7,9 @@ height: (@a * 10); .innest { - width: @a; + width: @a; .mix-inner((@a * 2)); - } + } } } diff --git a/test/less/mixins.less b/test/less/mixins.less index be9e2bbde..a94c3a65f 100644 --- a/test/less/mixins.less +++ b/test/less/mixins.less @@ -125,7 +125,7 @@ h3 { .margin_between(15px, 5px); } } .paddingFloat(@padding) { padding-left: @padding; } -.button { +.button { .paddingFloat(((10px + 12) * 2)); &.large { .paddingFloat(((10em * 2) * 2)); } diff --git a/test/less/property-name-interp.less b/test/less/property-name-interp.less index 9886e65fe..a4e5b5c7a 100644 --- a/test/less/property-name-interp.less +++ b/test/less/property-name-interp.less @@ -6,7 +6,7 @@ pi-test { @c_c: left; @d-d4: radius; @-: -; - + @{a}: 0; @{prefix}width: 50%; *-z-@{a} :1px dashed blue; @@ -34,7 +34,7 @@ pi-test { pre-property-@{suffix} +: middle; @{prefix}-property-@{suffix}+: low; @{prefix}-property-@{p} + : @v; - + @subterfuge: ~'+'; pre-property-ish@{subterfuge}: nice try dude; } diff --git a/test/less/scope.less b/test/less/scope.less index 475b1f6db..13d01a170 100644 --- a/test/less/scope.less +++ b/test/less/scope.less @@ -37,7 +37,7 @@ @local-will-be-made-global: green; .scope { scoped-val: @local-will-be-made-global; - } + } } } diff --git a/test/less/selectors.less b/test/less/selectors.less index 8b30546fd..e6418473c 100644 --- a/test/less/selectors.less +++ b/test/less/selectors.less @@ -41,9 +41,9 @@ a { .qux & { display: inline; } - .qux& { - display: inline-block; - } + .qux& { + display: inline-block; + } .qux & .biz { display: none; } @@ -123,7 +123,7 @@ a { } @num: 3; :nth-child(@{num}) { - selector: interpolated; + selector: interpolated; } .test { &:nth-child(odd):not(:nth-child(3)) { diff --git a/test/less/sourcemaps/basic.json b/test/less/sourcemaps/basic.json index 76a63c5a4..2bccdc51f 100644 --- a/test/less/sourcemaps/basic.json +++ b/test/less/sourcemaps/basic.json @@ -1,3 +1,3 @@ { - "my-color": "red" + "my-color": "red" } \ No newline at end of file diff --git a/test/less/variables.less b/test/less/variables.less index e896f4049..8674a372c 100644 --- a/test/less/variables.less +++ b/test/less/variables.less @@ -27,17 +27,17 @@ .redef { @var: 0; - .inition { - @var: 4; - @var: 2; - three: @var; - @var: 3; - } - zero: @var; + .inition { + @var: 4; + @var: 2; + three: @var; + @var: 3; + } + zero: @var; } .values { - minus-one: @var; + minus-one: @var; @a: 'Trebuchet'; @multi: 'A', B, C; font-family: @a, @a, @a; @@ -62,8 +62,8 @@ .testPollution { @a: 'no-pollution'; a: @a; - .polluteMixin(); - a: @a; + .polluteMixin(); + a: @a; } .units { diff --git a/test/less/whitespace.less b/test/less/whitespace.less index ab4804da9..050ac7340 100644 --- a/test/less/whitespace.less +++ b/test/less/whitespace.less @@ -1,8 +1,8 @@ .whitespace - { color: white; } - + { color: white; } + .whitespace { color: white; @@ -10,7 +10,7 @@ .whitespace { color: white; } -.whitespace{color:white;} +.whitespace{color:white;} .whitespace { color : white ; } .white, @@ -33,10 +33,10 @@ black; } .empty { - + } .sel -.newline_ws .tab_ws { +.newline_ws .tab_ws { color: white; background-position: 45 From 9fe311b8a93f5ba6e3579046c58f8dbe833acfe5 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Sat, 1 Mar 2014 20:00:49 +0200 Subject: [PATCH 029/161] Enable JSHint's trailing check. --- .jshintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.jshintrc b/.jshintrc index cb2794b45..d455a2e86 100644 --- a/.jshintrc +++ b/.jshintrc @@ -5,6 +5,7 @@ "node": true, "undef": true, "unused": "vars", + "trailing": true, "noarg": true, "eqnull": true, "forin": true From 7f34acd6d17f73ef8bc2e8bd8f4518ef5561ded9 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 2 Mar 2014 12:28:04 +0000 Subject: [PATCH 030/161] move visitors to a different namespace --- lib/less/non-node-index.js | 9 ++------- lib/less/parser.js | 10 +++++----- lib/less/{ => visitor}/extend-visitor.js | 6 +++--- lib/less/{ => visitor}/import-visitor.js | 12 ++++++------ lib/less/visitor/index.js | 13 +++++++++++++ lib/less/{ => visitor}/join-selector-visitor.js | 0 lib/less/{ => visitor}/to-css-visitor.js | 4 ++-- lib/less/{ => visitor}/visitor.js | 0 8 files changed, 31 insertions(+), 23 deletions(-) rename lib/less/{ => visitor}/extend-visitor.js (99%) rename lib/less/{ => visitor}/import-visitor.js (93%) create mode 100644 lib/less/visitor/index.js rename lib/less/{ => visitor}/join-selector-visitor.js (100%) rename lib/less/{ => visitor}/to-css-visitor.js (99%) rename lib/less/{ => visitor}/visitor.js (100%) diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index e3426a707..cacb207c7 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -6,16 +6,11 @@ var less = { }; less.tree = (require('./tree'))(less); -less.Parser = (require('./parser'))(less, less.tree); +less.visitor = require('./visitor/index.js')(less, less.tree); +less.Parser = (require('./parser'))(less, less.tree, less.visitor); less.tree.functions = (require('./functions'))(less, less.tree); require('./env')(less.tree); -less.tree.visitor = require('./visitor.js')(less.tree); -less.tree.importVisitor = require('./import-visitor.js')(less.tree); -less.tree.extendVisitor = require('./extend-visitor.js')(less.tree); -less.tree.joinSelectorVisitor = require('./join-selector-visitor.js')(less.tree.visitor); -less.tree.toCSSVisitor = require('./to-css-visitor.js')(less.tree); - less.tree.sourceMapOutput = require('./source-map-output.js')(less); module.exports = less; diff --git a/lib/less/parser.js b/lib/less/parser.js index b2ae22434..cdbdbe9b0 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,4 +1,4 @@ -module.exports = function(less, tree) { +module.exports = function(less, tree, visitor) { // // less.js - parser // @@ -557,9 +557,9 @@ var Parser = function Parser(env) { try { var preEvalVisitors = [], visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.extendVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) + new(visitor.joinSelectorVisitor)(), + new(visitor.extendVisitor)(), + new(visitor.toCSSVisitor)({compress: Boolean(options.compress)}) ], i, root = this; if (options.plugins) { @@ -679,7 +679,7 @@ var Parser = function Parser(env) { }; if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) + new visitor.importVisitor(this.imports, finish) .run(root); } else { return finish(); diff --git a/lib/less/extend-visitor.js b/lib/less/visitor/extend-visitor.js similarity index 99% rename from lib/less/extend-visitor.js rename to lib/less/visitor/extend-visitor.js index 0fd22a44a..bd636143c 100644 --- a/lib/less/extend-visitor.js +++ b/lib/less/visitor/extend-visitor.js @@ -1,8 +1,8 @@ -module.exports = function (tree) { +module.exports = function (visitor, tree) { /*jshint loopfunc:true */ var extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); + this._visitor = new visitor(this); this.contexts = []; this.allExtendsStack = [[]]; }; @@ -86,7 +86,7 @@ module.exports = function (tree) { }; var processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); + this._visitor = new visitor(this); }; processExtendsVisitor.prototype = { diff --git a/lib/less/import-visitor.js b/lib/less/visitor/import-visitor.js similarity index 93% rename from lib/less/import-visitor.js rename to lib/less/visitor/import-visitor.js index 3da88b534..91ea6c90f 100644 --- a/lib/less/import-visitor.js +++ b/lib/less/visitor/import-visitor.js @@ -1,6 +1,6 @@ -module.exports = function (tree) { - var importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); +module.exports = function (visitor, tree) { + var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { + this._visitor = new visitor(this); this._importer = importer; this._finish = finish; this.env = evalEnv || new tree.evalEnv(); @@ -16,7 +16,7 @@ module.exports = function (tree) { } }; - importVisitor.prototype = { + ImportVisitor.prototype = { isReplacing: true, run: function (root) { var error; @@ -92,7 +92,7 @@ module.exports = function (tree) { if (!inlineCSS && (env.importMultiple || !duplicateImport)) { importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) + new(ImportVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) .run(root); return; } @@ -138,5 +138,5 @@ module.exports = function (tree) { this.env.frames.shift(); } }; - return importVisitor; + return ImportVisitor; }; diff --git a/lib/less/visitor/index.js b/lib/less/visitor/index.js new file mode 100644 index 000000000..243910e6b --- /dev/null +++ b/lib/less/visitor/index.js @@ -0,0 +1,13 @@ +module.exports = function(less, tree) { + var visitor = require("./visitor")(tree), + visitors = { + visitor: visitor + }; + + visitors.importVisitor = require('./import-visitor.js')(visitor, tree); + visitors.extendVisitor = require('./extend-visitor.js')(visitor, tree); + visitors.joinSelectorVisitor = require('./join-selector-visitor.js')(visitor); + visitors.toCSSVisitor = require('./to-css-visitor.js')(visitor, tree); + return visitors; + +}; \ No newline at end of file diff --git a/lib/less/join-selector-visitor.js b/lib/less/visitor/join-selector-visitor.js similarity index 100% rename from lib/less/join-selector-visitor.js rename to lib/less/visitor/join-selector-visitor.js diff --git a/lib/less/to-css-visitor.js b/lib/less/visitor/to-css-visitor.js similarity index 99% rename from lib/less/to-css-visitor.js rename to lib/less/visitor/to-css-visitor.js index ad689e443..ad5cb4403 100644 --- a/lib/less/to-css-visitor.js +++ b/lib/less/visitor/to-css-visitor.js @@ -1,6 +1,6 @@ -module.exports = function (tree) { +module.exports = function (visitor, tree) { var toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); + this._visitor = new visitor(this); this._env = env; }; diff --git a/lib/less/visitor.js b/lib/less/visitor/visitor.js similarity index 100% rename from lib/less/visitor.js rename to lib/less/visitor/visitor.js From aa3abeb716c06842bff194e0fd50586f23c3f558 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 2 Mar 2014 17:51:31 +0000 Subject: [PATCH 031/161] fix trailing whitespace --- lib/less/environment/node.js | 4 ++-- lib/less/functions.js | 10 +++++----- lib/less/index.js | 6 +++--- lib/less/parser.js | 12 ++++++------ lib/less/tree.js | 6 +++--- lib/less/tree/color.js | 10 +++++----- lib/less/tree/mixin-call.js | 24 ++++++++++++------------ 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/lib/less/environment/node.js b/lib/less/environment/node.js index d4ef9c3c9..b8ec45e16 100644 --- a/lib/less/environment/node.js +++ b/lib/less/environment/node.js @@ -133,7 +133,7 @@ module.exports = { }, loadFile: function(env, filename, currentDirectory, callback) { var fullFilename, - data, + data, isUrl = isUrlRe.test( filename ); if (isUrl || isUrlRe.test(currentDirectory)) { @@ -206,4 +206,4 @@ module.exports = { } } } -}; \ No newline at end of file +}; diff --git a/lib/less/functions.js b/lib/less/functions.js index 3eef3f1ef..c0c1dc59e 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -342,7 +342,7 @@ var functions = { return new(tree.Dimension)(n.value * 100, '%'); }, color: function(c) { - if ((c instanceof tree.Quoted) && + if ((c instanceof tree.Quoted) && (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { return new(tree.Color)(c.value.slice(1)); } @@ -350,9 +350,9 @@ var functions = { c.keyword = undefined; return c; } - throw { - type: "Argument", - message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" + throw { + type: "Argument", + message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; }, iscolor: function (n) { @@ -729,5 +729,5 @@ tree.functionCall = function(env, currentFileInfo, environment) { tree.functionCall.prototype = functions; return functions; - + }; diff --git a/lib/less/index.js b/lib/less/index.js index f4730de63..043029efc 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -17,7 +17,7 @@ less.render = function (input, options, callback) { var css; try { css = root && root.toCSS && root.toCSS(options); - } + } catch (err) { callback(err); return; } callback(null, css); }); @@ -27,7 +27,7 @@ less.render = function (input, options, callback) { process.nextTick(function () { parser.parse(input, function (e, root) { if (e) { return ee.emit('error', e); } - try { ee.emit('success', root.toCSS(options)); } + try { ee.emit('success', root.toCSS(options)); } catch (err) { ee.emit('error', err); } }); }); @@ -83,7 +83,7 @@ less.formatError = function(ctx, options) { return message; }; - + less.writeError = function (ctx, options) { options = options || {}; if (options.silent) { return; } diff --git a/lib/less/parser.js b/lib/less/parser.js index c586feac8..b9be06c5d 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -81,20 +81,20 @@ var Parser = function Parser(env) { rootpath: currentFileInfo.rootpath, rootFilename: currentFileInfo.rootFilename }; - + less.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { if (e) { fileParsedFunc(e); return; } - // Pass on an updated rootpath if path of imported file is relative and file + // Pass on an updated rootpath if path of imported file is relative and file // is in a (sub|sup) directory - // - // Examples: + // + // Examples: // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', // then rootpath should become 'less/module/nav/' - // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' newFileInfo.currentDirectory = less.environment.getPath(env, resolvedFilename); if(newFileInfo.relativeUrls) { @@ -121,7 +121,7 @@ var Parser = function Parser(env) { new(less.Parser)(newEnv).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); }); - } + } }); } }; diff --git a/lib/less/tree.js b/lib/less/tree.js index 393f71b17..5073cda32 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -1,7 +1,7 @@ module.exports = function (less) { var tree = {}; - + tree.debugInfo = function(env, ctx, lineSeperator) { var result=""; if (env.dumpLineNumbers && !env.compress) { @@ -131,8 +131,8 @@ tree.Media = require('./tree/media')(tree); tree.UnicodeDescriptor = require('./tree/unicode-descriptor')(tree); tree.Negative = require('./tree/negative')(tree); tree.Extend = require('./tree/extend')(tree); -tree.RulesetCall = require('./tree/ruleset-call')(tree); +tree.RulesetCall = require('./tree/ruleset-call')(tree); return tree; -}; \ No newline at end of file +}; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index bbeb542a4..533235f85 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -43,7 +43,7 @@ Color.prototype = { }, toCSS: function (env, doNotCompress) { var compress = env && env.compress && !doNotCompress, color, alpha; - + // `keyword` is set if this color was originally // converted from a named color string so we need // to respect this and try to output named color too. @@ -61,8 +61,8 @@ Color.prototype = { return clamp(Math.round(c), 255); }).concat(clamp(alpha, 1)) .join(',' + (compress ? '' : ' ')) + ")"; - } - + } + color = this.toRGB(); if (compress) { @@ -169,9 +169,9 @@ Color.fromKeyword = function(keyword) { c = new(Color)(data.colors[key].slice(1)); } else if (key === "transparent") { - c = new(Color)([0, 0, 0], 0); + c = new(Color)([0, 0, 0], 0); } - + if (c) { c.keyword = keyword; return c; diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js index 606600aeb..de514e79e 100644 --- a/lib/less/tree/mixin-call.js +++ b/lib/less/tree/mixin-call.js @@ -20,7 +20,7 @@ Call.prototype = { eval: function (env) { var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; + defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count; args = this.arguments && this.arguments.map(function (a) { return { name: a.name, value: a.value.eval(env) }; @@ -29,12 +29,12 @@ Call.prototype = { for (i = 0; i < env.frames.length; i++) { if ((mixins = env.frames[i].find(this.selector)).length > 0) { isOneFound = true; - + // 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. - + for (m = 0; m < mixins.length; m++) { mixin = mixins[m]; isRecursive = false; @@ -47,11 +47,11 @@ Call.prototype = { if (isRecursive) { continue; } - - if (mixin.matchArgs(args, env)) { + + if (mixin.matchArgs(args, env)) { candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { + + if (mixin.matchCondition) { for (f = 0; f < 2; f++) { defaultFunc.value(f); conditionResult[f] = mixin.matchCondition(args, env); @@ -63,16 +63,16 @@ Call.prototype = { } candidates.push(candidate); - } + } } else { candidates.push(candidate); } - + match = true; } } - + defaultFunc.reset(); count = [0, 0, 0]; @@ -91,7 +91,7 @@ Call.prototype = { index: this.index, filename: this.currentFileInfo.filename }; } } - + for (m = 0; m < candidates.length; m++) { candidate = candidates[m].group; if ((candidate === defNone) || (candidate === defaultResult)) { @@ -108,7 +108,7 @@ Call.prototype = { } } } - + if (match) { if (!this.currentFileInfo || !this.currentFileInfo.reference) { for (i = 0; i < rules.length; i++) { From b2820b09f4747e049ca73ce6a3029b21dfb13720 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Tue, 20 May 2014 15:36:04 +0100 Subject: [PATCH 032/161] Return a promise rather than an event emitter [discussed in #1902] --- lib/less/index.js | 15 ++++++--------- package.json | 3 ++- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/less/index.js b/lib/less/index.js index 043029efc..058bfd958 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,3 +1,4 @@ +var Promise = require('promise'); var less = require("./non-node-index.js"); less.render = function (input, options, callback) { @@ -8,8 +9,7 @@ less.render = function (input, options, callback) { options = {}; } - var parser = new(less.Parser)(options), - ee; + var parser = new(less.Parser)(options); if (callback) { parser.parse(input, function (e, root) { @@ -22,16 +22,13 @@ less.render = function (input, options, callback) { callback(null, css); }); } else { - ee = new (require('events').EventEmitter)(); - - process.nextTick(function () { + return new Promise(function (resolve, reject) { parser.parse(input, function (e, root) { - if (e) { return ee.emit('error', e); } - try { ee.emit('success', root.toCSS(options)); } - catch (err) { ee.emit('error', err); } + if (e) { return reject(e); } + try { resolve(root.toCSS(options)); } + catch (err) { reject( err); } }); }); - return ee; } }; less.formatError = function(ctx, options) { diff --git a/package.json b/package.json index 747b8758c..65f8e47fe 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "request": ">=2.33.0", "mkdirp": "~0.3.5", "clean-css": "2.1.x", - "source-map": "0.1.x" + "source-map": "0.1.x", + "promise": "~5.0.0" }, "devDependencies": { "diff": "~1.0", From 815d609223adbfb588ee3dbdb19454c56e42a9f5 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Tue, 20 May 2014 15:39:04 +0100 Subject: [PATCH 033/161] Use native Promises when available --- lib/less/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/less/index.js b/lib/less/index.js index 058bfd958..879a64d79 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,4 +1,4 @@ -var Promise = require('promise'); +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; var less = require("./non-node-index.js"); less.render = function (input, options, callback) { @@ -22,7 +22,7 @@ less.render = function (input, options, callback) { callback(null, css); }); } else { - return new Promise(function (resolve, reject) { + return new PromiseConstructor(function (resolve, reject) { parser.parse(input, function (e, root) { if (e) { return reject(e); } try { resolve(root.toCSS(options)); } From 07f9ba258efa8bf074c5d53a34f04823da301ba8 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Tue, 20 May 2014 16:12:18 +0100 Subject: [PATCH 034/161] Move chunker and LessError into separate files This begins the process of cleaning up parser.js [see #1902] --- lib/less/chunker.js | 123 ++++++++++++++++++++++++++++++ lib/less/less-error.js | 28 +++++++ lib/less/parser.js | 168 ++++------------------------------------- 3 files changed, 165 insertions(+), 154 deletions(-) create mode 100644 lib/less/chunker.js create mode 100644 lib/less/less-error.js diff --git a/lib/less/chunker.js b/lib/less/chunker.js new file mode 100644 index 000000000..447391730 --- /dev/null +++ b/lib/less/chunker.js @@ -0,0 +1,123 @@ +var LessError = require('./less-error.js'); + +// Split the input into chunks. +module.exports = function (parser, input) { + var len = input.length, level = 0, parenLevel = 0, + lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, + chunks = [], emitFrom = 0, + parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; + + function fail(msg, index) { + throw new(LessError)(parser, { + index: index || parserCurrentIndex, + type: 'Parse', + message: msg, + filename: env.currentFileInfo.filename + }, env); + } + + function emitChunk(force) { + var len = parserCurrentIndex - emitFrom; + if (((len < 512) && !force) || !len) { + return; + } + chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); + emitFrom = parserCurrentIndex + 1; + } + + for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { + cc = input.charCodeAt(parserCurrentIndex); + if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { + // a-z or whitespace + continue; + } + + switch (cc) { + case 40: // ( + parenLevel++; + lastOpeningParen = parserCurrentIndex; + continue; + case 41: // ) + if (--parenLevel < 0) { + return fail("missing opening `(`"); + } + continue; + case 59: // ; + if (!parenLevel) { emitChunk(); } + continue; + case 123: // { + level++; + lastOpening = parserCurrentIndex; + continue; + case 125: // } + if (--level < 0) { + return fail("missing opening `{`"); + } + if (!level && !parenLevel) { emitChunk(); } + continue; + case 92: // \ + if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } + return fail("unescaped `\\`"); + case 34: + case 39: + case 96: // ", ' and ` + matched = 0; + currentChunkStartIndex = parserCurrentIndex; + for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { + cc2 = input.charCodeAt(parserCurrentIndex); + if (cc2 > 96) { continue; } + if (cc2 == cc) { matched = 1; break; } + if (cc2 == 92) { // \ + if (parserCurrentIndex == len - 1) { + return fail("unescaped `\\`"); + } + parserCurrentIndex++; + } + } + if (matched) { continue; } + return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); + case 47: // /, check for comment + if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } + cc2 = input.charCodeAt(parserCurrentIndex + 1); + if (cc2 == 47) { + // //, find lnfeed + for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { + cc2 = input.charCodeAt(parserCurrentIndex); + if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } + } + } else if (cc2 == 42) { + // /*, find */ + lastMultiComment = currentChunkStartIndex = parserCurrentIndex; + for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { + cc2 = input.charCodeAt(parserCurrentIndex); + if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } + if (cc2 != 42) { continue; } + if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } + } + if (parserCurrentIndex == len - 1) { + return fail("missing closing `*/`", currentChunkStartIndex); + } + parserCurrentIndex++; + } + continue; + case 42: // *, check for unmatched */ + if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { + return fail("unmatched `/*`"); + } + continue; + } + } + + if (level !== 0) { + if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { + return fail("missing closing `}` or `*/`", lastOpening); + } else { + return fail("missing closing `}`", lastOpening); + } + } else if (parenLevel !== 0) { + return fail("missing closing `)`", lastOpeningParen); + } + + emitChunk(true); + return chunks; +}; \ No newline at end of file diff --git a/lib/less/less-error.js b/lib/less/less-error.js new file mode 100644 index 000000000..e7a0f6131 --- /dev/null +++ b/lib/less/less-error.js @@ -0,0 +1,28 @@ + +module.exports = LessError; +function LessError(parser, e, env) { + var input = parser.getInput(e, env), + loc = parser.getLocation(e.index, input), + line = loc.line, + col = loc.column, + callLine = e.call && parser.getLocation(e.call, input).line, + lines = input.split('\n'); + + this.type = e.type || 'Syntax'; + this.message = e.message; + this.filename = e.filename || env.currentFileInfo.filename; + this.index = e.index; + this.line = typeof(line) === 'number' ? line + 1 : null; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; + this.stack = e.stack; + this.column = col; + this.extract = [ + lines[line - 1], + lines[line], + lines[line + 1] + ]; +} + +LessError.prototype = new Error(); +LessError.prototype.constructor = LessError; \ No newline at end of file diff --git a/lib/less/parser.js b/lib/less/parser.js index b9be06c5d..e3b2a9adb 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,3 +1,6 @@ +var chunker = require('./chunker.js'); +var LessError = require('./less-error.js'); + module.exports = function(less, tree, visitor) { // // less.js - parser @@ -320,33 +323,6 @@ var Parser = function Parser(env) { }; } - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - this.env = env = env || {}; // @@ -379,130 +355,10 @@ var Parser = function Parser(env) { input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; parser.imports.contents[env.currentFileInfo.filename] = str; - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); + try { + chunks = chunker(parser, str); + } catch (ex) { + return callback(new LessError(parser, error, env)); } current = chunks[0]; @@ -516,7 +372,7 @@ var Parser = function Parser(env) { root.root = true; root.firstRoot = true; } catch (e) { - return callback(new(LessError)(e, env)); + return callback(new LessError(parser, e, env)); } root.toCSS = (function (evaluate) { @@ -609,7 +465,7 @@ var Parser = function Parser(env) { strictUnits: Boolean(options.strictUnits), numPrecision: 8}); } catch (e) { - throw new(LessError)(e, env); + throw new LessError(parser, e, env); } var CleanCSS = less.environment.getCleanCSS(); @@ -668,7 +524,7 @@ var Parser = function Parser(env) { if (e) { if (!(e instanceof LessError)) { - e = new(LessError)(e, env); + e = new LessError(parser, e, env); } return callback(e); @@ -2053,6 +1909,10 @@ var Parser = function Parser(env) { } } }; + + parser.getInput = getInput; + parser.getLocation = getLocation; + return parser; }; Parser.serializeVars = function(vars) { From 8e7274fad42f7f2d9460a3b6512283faffd3b7c8 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Fri, 6 Jun 2014 08:44:25 +0100 Subject: [PATCH 035/161] 2.0.0 will require 0.10 or above of node. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 747b8758c..af6f50033 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "main": "./dist/less-1.6.3.js" }, "engines": { - "node": ">=0.8.0" + "node": ">=0.10.0" }, "scripts": { "test": "grunt test" From 8c978cd7671af91332a39ffbefcc581626e1072b Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Fri, 6 Jun 2014 10:58:29 +0100 Subject: [PATCH 036/161] Pass env to chunker for use in error messages --- lib/less/chunker.js | 2 +- lib/less/parser.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/less/chunker.js b/lib/less/chunker.js index 447391730..0d1388201 100644 --- a/lib/less/chunker.js +++ b/lib/less/chunker.js @@ -1,7 +1,7 @@ var LessError = require('./less-error.js'); // Split the input into chunks. -module.exports = function (parser, input) { +module.exports = function (parser, input, env) { var len = input.length, level = 0, parenLevel = 0, lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, chunks = [], emitFrom = 0, diff --git a/lib/less/parser.js b/lib/less/parser.js index e3b2a9adb..97ccc8f47 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -356,7 +356,7 @@ var Parser = function Parser(env) { parser.imports.contents[env.currentFileInfo.filename] = str; try { - chunks = chunker(parser, str); + chunks = chunker(parser, str, env); } catch (ex) { return callback(new LessError(parser, error, env)); } From 78a92e702daedff605c76be0eeebaeb10241fcd1 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Fri, 6 Jun 2014 11:00:08 +0100 Subject: [PATCH 037/161] Fix LessError being used before being defined --- lib/less/less-error.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/less/less-error.js b/lib/less/less-error.js index e7a0f6131..505cbc89f 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -1,6 +1,5 @@ -module.exports = LessError; -function LessError(parser, e, env) { +var LessError = module.exports = function LessError(parser, e, env) { var input = parser.getInput(e, env), loc = parser.getLocation(e.index, input), line = loc.line, @@ -25,4 +24,4 @@ function LessError(parser, e, env) { } LessError.prototype = new Error(); -LessError.prototype.constructor = LessError; \ No newline at end of file +LessError.prototype.constructor = LessError; From be151fb21d36a65f1cc05a9f1671a12712a43577 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Fri, 6 Jun 2014 11:00:54 +0100 Subject: [PATCH 038/161] Add missing semicolon --- lib/less/less-error.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less/less-error.js b/lib/less/less-error.js index 505cbc89f..750bad806 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -21,7 +21,7 @@ var LessError = module.exports = function LessError(parser, e, env) { lines[line], lines[line + 1] ]; -} +}; LessError.prototype = new Error(); LessError.prototype.constructor = LessError; From 01a5d7cd8c25cf1c350ee360a72795686070e8b5 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Fri, 6 Jun 2014 11:03:06 +0100 Subject: [PATCH 039/161] Fix incorrect variable name for error --- lib/less/parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less/parser.js b/lib/less/parser.js index 97ccc8f47..735102f5c 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -358,7 +358,7 @@ var Parser = function Parser(env) { try { chunks = chunker(parser, str, env); } catch (ex) { - return callback(new LessError(parser, error, env)); + return callback(new LessError(parser, ex, env)); } current = chunks[0]; From 9d987ab96e15899a32f92447c35206018a923df2 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Fri, 6 Jun 2014 11:31:51 +0100 Subject: [PATCH 040/161] Update jshintrc --- .jshintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.jshintrc b/.jshintrc index d455a2e86..5c31e91cf 100644 --- a/.jshintrc +++ b/.jshintrc @@ -8,5 +8,6 @@ "trailing": true, "noarg": true, "eqnull": true, - "forin": true + "forin": true, + "predef": ["Promise"] } From d0f2305b1219b1edb6a1dcd6239c77b9d6d01642 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 10 Jun 2014 19:34:33 +0100 Subject: [PATCH 041/161] tidy merge - fix mistake in lessc and remove dist files merged in --- bin/lessc | 2 +- dist/less-1.7.1.js | 7936 ------------------------------- dist/less-1.7.1.min.js | 16 - dist/less-rhino-1.7.1.js | 9311 ------------------------------------- dist/lessc-rhino-1.7.1.js | 449 -- 5 files changed, 1 insertion(+), 17713 deletions(-) delete mode 100644 dist/less-1.7.1.js delete mode 100644 dist/less-1.7.1.min.js delete mode 100644 dist/less-rhino-1.7.1.js delete mode 100644 dist/lessc-rhino-1.7.1.js diff --git a/bin/lessc b/bin/lessc index 3a7384ec0..1fb019344 100755 --- a/bin/lessc +++ b/bin/lessc @@ -1,7 +1,7 @@ #!/usr/bin/env node var path = require('path'), - fs = require('../lib/less/fs'), + fs = require('../lib/less/environment/node/fs'), sys = require('util'), os = require('os'), mkdirp; diff --git a/dist/less-1.7.1.js b/dist/less-1.7.1.js deleted file mode 100644 index 5f7f4b58b..000000000 --- a/dist/less-1.7.1.js +++ /dev/null @@ -1,7936 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.1 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (!css.styleSheet) { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // For IE. - // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. - // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head - if (css.styleSheet) { - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; - } - return styles; -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.7.1.min.js b/dist/less-1.7.1.min.js deleted file mode 100644 index 03b36452c..000000000 --- a/dist/less-1.7.1.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.1 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet||(j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue);var k=document.getElementsByTagName("head")[0];if(null===h||i===!1){var l=b&&b.nextSibling||null;l?l.parentNode.insertBefore(j,l):k.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),j.styleSheet)try{j.styleSheet.cssText=a}catch(m){throw new Error("Couldn't reassign styleSheet.cssText.")}if(c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(m){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;dD&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,g=y;d();var h=j(/^@import?\s+/),i=(h?this.importOptions():null)||{};return h&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),k(";"))?(f(),c=c&&new x.Value(c),new x.Import(b,c,i,g,a.currentFileInfo)):void e()},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(c=k("/")||k("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(y+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=C,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(Math.round(b*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("./fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)t.value(h),s[h]=d.matchCondition(e,b);(s[0]||s[1])&&(s[0]!=s[1]&&(l.group=s[1]?v:w),r.push(l))}else r.push(l);q=!0}}for(t.reset(),n=[0,0,0],g=0;g0)m=w;else if(m=v,n[v]+n[w]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gh;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;ii;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames?this.frames.concat(c.frames):c.frames),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b,c;return"Quoted"!==a.type||this.escaped||a.escaped?(b=this.toCSS(),c=a.toCSS()):(b=a.value,c=this.value),b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value||c instanceof a.Ruleset?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d,e=!1,f=this.name;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:b(c,f)),"font"!==f||c.strictMath||(e=!0,c.strictMath=!0);try{if(d=this.value.eval(c),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(g){throw"number"!=typeof g.index&&(g.index=this.index,g.filename=this.currentFileInfo.filename),g}finally{e&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;fb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),h.importMultiple||(b.skip=g?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};if(d){b.root=d,b.importedFilename=i;var k=g||i in e.recursionDetector;if(!f&&(h.importMultiple||!k))return e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,j,h,e.onceFileDetectionMap,e.recursionDetector).run(d)}j()})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++); -return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var d,e=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?d=this._sourceMapURL:this._sourceMapFilename&&(d=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(e):d="data:application/json,"+encodeURIComponent(e),d&&this._css.push("/*# sourceMappingURL="+d+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0 && result[result.length-1] !== '..') { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']'; - } else { - return obj.toCSS(false); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/lessc-rhino-1.7.1.js b/dist/lessc-rhino-1.7.1.js deleted file mode 100644 index ed0146bda..000000000 --- a/dist/lessc-rhino-1.7.1.js +++ /dev/null @@ -1,449 +0,0 @@ -/* Less.js v1.7.1 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - var message = formatError(ctx, options); - throw new Error(message); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } -}(arguments)); From 94f746b5e754b88ced6605781c94c7acb4d22e20 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 19 Jun 2014 18:23:19 +0100 Subject: [PATCH 042/161] move some dependencies out of chunker. make chunker optional. start fixing error messages chunker handled --- lib/less/chunker.js | 77 ++++++++----------- lib/less/env.js | 1 + lib/less/parser.js | 43 +++++++++-- test/less/errors/import-subfolder2.txt | 2 +- .../less/errors/parse-error-curly-bracket.txt | 2 +- test/less/errors/parse-error-extra-parens.txt | 2 +- .../errors/parse-error-missing-bracket.txt | 2 +- .../errors/parse-error-missing-parens.txt | 2 +- 8 files changed, 75 insertions(+), 56 deletions(-) diff --git a/lib/less/chunker.js b/lib/less/chunker.js index 0d1388201..a5ccaa969 100644 --- a/lib/less/chunker.js +++ b/lib/less/chunker.js @@ -1,32 +1,21 @@ -var LessError = require('./less-error.js'); - // Split the input into chunks. -module.exports = function (parser, input, env) { +module.exports = function (input, fail) { var len = input.length, level = 0, parenLevel = 0, lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - throw new(LessError)(parser, { - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } + chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched; function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; + var len = chunkerCurrentIndex - emitFrom; if (((len < 512) && !force) || !len) { return; } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; + chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1)); + emitFrom = chunkerCurrentIndex + 1; } - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); + for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc = input.charCodeAt(chunkerCurrentIndex); if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { // a-z or whitespace continue; @@ -35,11 +24,11 @@ module.exports = function (parser, input, env) { switch (cc) { case 40: // ( parenLevel++; - lastOpeningParen = parserCurrentIndex; + lastOpeningParen = chunkerCurrentIndex; continue; case 41: // ) if (--parenLevel < 0) { - return fail("missing opening `(`"); + return fail("missing opening `(`", chunkerCurrentIndex); } continue; case 59: // ; @@ -47,62 +36,62 @@ module.exports = function (parser, input, env) { continue; case 123: // { level++; - lastOpening = parserCurrentIndex; + lastOpening = chunkerCurrentIndex; continue; case 125: // } if (--level < 0) { - return fail("missing opening `{`"); + return fail("missing opening `{`", chunkerCurrentIndex); } if (!level && !parenLevel) { emitChunk(); } continue; case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); + if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; } + return fail("unescaped `\\`", chunkerCurrentIndex); case 34: case 39: case 96: // ", ' and ` matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); + currentChunkStartIndex = chunkerCurrentIndex; + for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); if (cc2 > 96) { continue; } if (cc2 == cc) { matched = 1; break; } if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); + if (chunkerCurrentIndex == len - 1) { + return fail("unescaped `\\`", chunkerCurrentIndex); } - parserCurrentIndex++; + chunkerCurrentIndex++; } } if (matched) { continue; } return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); + if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; } + cc2 = input.charCodeAt(chunkerCurrentIndex + 1); if (cc2 == 47) { // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); + for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } } } else if (cc2 == 42) { // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } + lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex; + for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; } if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } + if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; } } - if (parserCurrentIndex == len - 1) { + if (chunkerCurrentIndex == len - 1) { return fail("missing closing `*/`", currentChunkStartIndex); } - parserCurrentIndex++; + chunkerCurrentIndex++; } continue; case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); + if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) { + return fail("unmatched `/*`", chunkerCurrentIndex); } continue; } @@ -120,4 +109,4 @@ module.exports = function (parser, input, env) { emitChunk(true); return chunks; -}; \ No newline at end of file +}; diff --git a/lib/less/env.js b/lib/less/env.js index 801929410..8e964a796 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -14,6 +14,7 @@ module.exports = function (tree) { 'processImports', // option - whether to process imports. if false then imports will not be imported 'syncImport', // option - whether to import synchronously 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true + 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. 'mime', // browser only - mime type for sheet import 'useFileCache', // browser only - whether to use the per file session cache 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. diff --git a/lib/less/parser.js b/lib/less/parser.js index 22e4a9cd2..7341edd33 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -355,10 +355,30 @@ var Parser = function Parser(env) { input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; parser.imports.contents[env.currentFileInfo.filename] = str; - try { - chunks = chunker(parser, str, env); - } catch (ex) { - return callback(new LessError(parser, ex, env)); + // chunking makes things quicker + // but it is a non-perfect parse - it can't recognise + // unquoted urls, meaning it can't distinguish comments + // meaning comments with quotes or {}() in them get 'counted' + // and then lead to parse errors. + // In addition if the chunking chunks in the wrong place we might + // not be able to parse a parser statement in one go + // this is officially deprecated but can be switched on via an option + // in the case it causes too much performance issues. + if (env.chunkInput) { + try { + chunks = chunker(str, function fail(msg, index) { + throw new(LessError)(parser, { + index: index, + type: 'Parse', + message: msg, + filename: env.currentFileInfo.filename + }, env); + }); + } catch (ex) { + return callback(new LessError(parser, ex, env)); + } + } else { + chunks = [str]; } current = chunks[0]; @@ -499,14 +519,23 @@ var Parser = function Parser(env) { // We split it up into two parts (the part which parsed, // and the part which didn't), so we can color them differently. if (i < input.length - 1) { - i = furthest; + i = Math.max(i, furthest); + + var message = "Unrecognised input"; + + if (input[i] === '}') { + message = "Missing opening '{'"; + } else if (input[i] === ')') { + message = "Missing opening '('"; + } + var loc = getLocation(i, input); lines = input.split('\n'); line = loc.line + 1; error = { type: "Parse", - message: "Unrecognised input", + message: message, index: i, filename: env.currentFileInfo.filename, line: line, @@ -822,7 +851,7 @@ var Parser = function Parser(env) { colorCandidateString = colorCandidateString[1]; if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters error("Invalid HEX color code"); - } + } return new(tree.Color)(rgb[1]); } }, diff --git a/test/less/errors/import-subfolder2.txt b/test/less/errors/import-subfolder2.txt index b5b1a69ba..bb7716308 100644 --- a/test/less/errors/import-subfolder2.txt +++ b/test/less/errors/import-subfolder2.txt @@ -1,4 +1,4 @@ -ParseError: missing opening `{` in {path}parse-error-curly-bracket.less on line 4, column 1: +ParseError: Missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1: 3 } 4 } 5 diff --git a/test/less/errors/parse-error-curly-bracket.txt b/test/less/errors/parse-error-curly-bracket.txt index b5b1a69ba..bb7716308 100644 --- a/test/less/errors/parse-error-curly-bracket.txt +++ b/test/less/errors/parse-error-curly-bracket.txt @@ -1,4 +1,4 @@ -ParseError: missing opening `{` in {path}parse-error-curly-bracket.less on line 4, column 1: +ParseError: Missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1: 3 } 4 } 5 diff --git a/test/less/errors/parse-error-extra-parens.txt b/test/less/errors/parse-error-extra-parens.txt index 5c1aaef2a..4eccf1b84 100644 --- a/test/less/errors/parse-error-extra-parens.txt +++ b/test/less/errors/parse-error-extra-parens.txt @@ -1,3 +1,3 @@ -ParseError: missing opening `(` in {path}parse-error-extra-parens.less on line 1, column 24: +ParseError: Missing opening '(' in {path}parse-error-extra-parens.less on line 1, column 24: 1 @media (extra: bracket)) { 2 body { diff --git a/test/less/errors/parse-error-missing-bracket.txt b/test/less/errors/parse-error-missing-bracket.txt index 7db2716ed..a516a0acf 100644 --- a/test/less/errors/parse-error-missing-bracket.txt +++ b/test/less/errors/parse-error-missing-bracket.txt @@ -1,3 +1,3 @@ -ParseError: missing closing `}` in {path}parse-error-missing-bracket.less on line 1, column 6: +ParseError: Missing closing '}' in {path}parse-error-missing-bracket.less on line 1, column 6: 1 body { 2 background-color: #fff; diff --git a/test/less/errors/parse-error-missing-parens.txt b/test/less/errors/parse-error-missing-parens.txt index a7a67067a..f92da4588 100644 --- a/test/less/errors/parse-error-missing-parens.txt +++ b/test/less/errors/parse-error-missing-parens.txt @@ -1,3 +1,3 @@ -ParseError: missing closing `)` in {path}parse-error-missing-parens.less on line 1, column 8: +ParseError: Missing closing ')' in {path}parse-error-missing-parens.less on line 1, column 8: 1 @media (missing: bracket { 2 body { From 73c34ac24f4c0dccc683c5e93e5f13cd4454e4ab Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 22 Jun 2014 10:43:05 +0100 Subject: [PATCH 043/161] improve some of the error messages when not using the chunker. --- lib/less/index.js | 2 +- lib/less/parser.js | 66 +++++++++++++------ test/less/errors/detached-ruleset-2.txt | 2 +- test/less/errors/detached-ruleset-4.txt | 5 +- test/less/errors/detached-ruleset-6.txt | 2 +- test/less/errors/import-no-semi.txt | 2 +- test/less/errors/import-subfolder2.txt | 2 +- .../less/errors/parse-error-curly-bracket.txt | 2 +- test/less/errors/parse-error-extra-parens.txt | 2 +- .../errors/parse-error-missing-bracket.txt | 4 +- .../errors/parse-error-missing-parens.txt | 2 +- .../errors/property-asterisk-only-name.txt | 2 +- test/less/errors/property-ie5-hack.txt | 3 +- 13 files changed, 61 insertions(+), 35 deletions(-) diff --git a/lib/less/index.js b/lib/less/index.js index 879a64d79..5d4a0bd13 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -54,7 +54,7 @@ less.formatError = function(ctx, options) { var errorTxt = ctx.line + ' '; if (extract[1]) { errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + + stylize(stylize(stylize(extract[1].substr(ctx.column, 1), 'bold') + extract[1].slice(ctx.column + 1), 'red'), 'inverse'); } error.push(errorTxt); diff --git a/lib/less/parser.js b/lib/less/parser.js index 7341edd33..bdc0a1fca 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -41,6 +41,7 @@ var Parser = function Parser(env) { j, // current chunk saveStack = [], // holds state for backtracking furthest, // furthest index the parser has gone to + furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause chunks, // chunkified input current, // current chunk currentPos, // index of current chunk, in `input` @@ -129,9 +130,23 @@ var Parser = function Parser(env) { } }; - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } + function save() { + currentPos = i; + saveStack.push( { current: current, i: i, j: j }); + } + function restore(possibleErrorMessage) { + if (i > furthest) { + furthest = i; + furthestPossibleErrorMessage = possibleErrorMessage; + } + var state = saveStack.pop(); + current = state.current; + currentPos = i = state.i; + j = state.j; + } + function forget() { + saveStack.pop(); + } function sync() { if (i > currentPos) { @@ -519,14 +534,21 @@ var Parser = function Parser(env) { // We split it up into two parts (the part which parsed, // and the part which didn't), so we can color them differently. if (i < input.length - 1) { - i = Math.max(i, furthest); var message = "Unrecognised input"; + if (i < furthest) { + message = furthestPossibleErrorMessage || message; + i = furthest; + } - if (input[i] === '}') { - message = "Missing opening '{'"; - } else if (input[i] === ')') { - message = "Missing opening '('"; + if (!furthestPossibleErrorMessage) { + if (input[i] === '}') { + message += ". Possibly missing opening '{'"; + } else if (input[i] === ')') { + message += ". Possibly missing opening '('"; + } else if (i >= input.length - 1) { + message += ". Possibly missing something"; + } } var loc = getLocation(i, input); @@ -1185,8 +1207,7 @@ var Parser = function Parser(env) { // .mixincall(@a: {rule: set;}); // so we have to be nice and restore if (!$char(')')) { - furthest = i; - restore(); + restore("Missing closing ')'"); return; } @@ -1275,7 +1296,7 @@ var Parser = function Parser(env) { e = new(tree.Paren)(v); forget(); } else { - restore(); + restore("Missing closing ')'"); } } else { forget(); @@ -1434,8 +1455,6 @@ var Parser = function Parser(env) { } return ruleset; } else { - // Backtrack - furthest = i; restore(); } }, @@ -1473,7 +1492,6 @@ var Parser = function Parser(env) { forget(); return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); } else { - furthest = i; restore(); if (value && !tryAnonymous) { return this.rule(true); @@ -1518,9 +1536,11 @@ var Parser = function Parser(env) { features = features && new(tree.Value)(features); return new(tree.Import)(path, features, options, index, env.currentFileInfo); } + restore("bad media features or missing ';'"); + return; } - restore(); + restore("malformed import"); }, importOptions: function() { @@ -1560,6 +1580,7 @@ var Parser = function Parser(env) { mediaFeature: function () { var entities = this.entities, nodes = [], e, p; + save(); do { e = entities.keyword() || entities.variable(); if (e) { @@ -1573,12 +1594,17 @@ var Parser = function Parser(env) { } else if (e) { nodes.push(new(tree.Paren)(e)); } else { + restore("badly formed media feature definition"); return null; } - } else { return null; } + } else { + restore("Missing closing ')'"); + return null; + } } } while (e); + forget(); if (nodes.length > 0) { return new(tree.Expression)(nodes); } @@ -1720,7 +1746,7 @@ var Parser = function Parser(env) { env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); } - restore(); + restore("directive options not recognised"); }, // @@ -1933,9 +1959,9 @@ var Parser = function Parser(env) { } for (k = 0; k < name.length; k++) { s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), + name[k] = (s.charAt(0) !== '@') ? + new(tree.Keyword)(s) : + new(tree.Variable)('@' + s.slice(2, -1), index[k], env.currentFileInfo); } return name; diff --git a/test/less/errors/detached-ruleset-2.txt b/test/less/errors/detached-ruleset-2.txt index f18e09353..32344a8e2 100644 --- a/test/less/errors/detached-ruleset-2.txt +++ b/test/less/errors/detached-ruleset-2.txt @@ -1,4 +1,4 @@ -ParseError: Unrecognised input in {path}detached-ruleset-2.less on line 5, column 3: +ParseError: Unrecognised input. Possibly missing opening '(' in {path}detached-ruleset-2.less on line 5, column 9: 4 .a { 5 a: @a(); 6 } diff --git a/test/less/errors/detached-ruleset-4.txt b/test/less/errors/detached-ruleset-4.txt index d6d6526d4..905222eb8 100644 --- a/test/less/errors/detached-ruleset-4.txt +++ b/test/less/errors/detached-ruleset-4.txt @@ -1,3 +1,4 @@ -ParseError: Unrecognised input in {path}detached-ruleset-4.less on line 1, column 18: -1 .mixin-definition(@a: { +ParseError: Unrecognised input in {path}detached-ruleset-4.less on line 3, column 4: 2 b: 1; +3 }) { +4 @a(); diff --git a/test/less/errors/detached-ruleset-6.txt b/test/less/errors/detached-ruleset-6.txt index 07840445e..7f6b0aeb3 100644 --- a/test/less/errors/detached-ruleset-6.txt +++ b/test/less/errors/detached-ruleset-6.txt @@ -1,4 +1,4 @@ -ParseError: Unrecognised input in {path}detached-ruleset-6.less on line 2, column 3: +ParseError: Unrecognised input in {path}detached-ruleset-6.less on line 2, column 6: 1 .a { 2 b: { 3 color: red; diff --git a/test/less/errors/import-no-semi.txt b/test/less/errors/import-no-semi.txt index 8b3f795cf..d373cf44a 100644 --- a/test/less/errors/import-no-semi.txt +++ b/test/less/errors/import-no-semi.txt @@ -1,2 +1,2 @@ -ParseError: Unrecognised input in {path}import-no-semi.less on line 1, column 1: +ParseError: bad media features or missing ';' in {path}import-no-semi.less on line 1, column 41: 1 @import "this-statement-is-invalid.less" diff --git a/test/less/errors/import-subfolder2.txt b/test/less/errors/import-subfolder2.txt index bb7716308..59cf5bd9f 100644 --- a/test/less/errors/import-subfolder2.txt +++ b/test/less/errors/import-subfolder2.txt @@ -1,4 +1,4 @@ -ParseError: Missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1: +ParseError: Unrecognised input. Possibly missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1: 3 } 4 } 5 diff --git a/test/less/errors/parse-error-curly-bracket.txt b/test/less/errors/parse-error-curly-bracket.txt index bb7716308..59cf5bd9f 100644 --- a/test/less/errors/parse-error-curly-bracket.txt +++ b/test/less/errors/parse-error-curly-bracket.txt @@ -1,4 +1,4 @@ -ParseError: Missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1: +ParseError: Unrecognised input. Possibly missing opening '{' in {path}parse-error-curly-bracket.less on line 4, column 1: 3 } 4 } 5 diff --git a/test/less/errors/parse-error-extra-parens.txt b/test/less/errors/parse-error-extra-parens.txt index 4eccf1b84..9e79065d6 100644 --- a/test/less/errors/parse-error-extra-parens.txt +++ b/test/less/errors/parse-error-extra-parens.txt @@ -1,3 +1,3 @@ -ParseError: Missing opening '(' in {path}parse-error-extra-parens.less on line 1, column 24: +ParseError: Unrecognised input. Possibly missing opening '(' in {path}parse-error-extra-parens.less on line 1, column 24: 1 @media (extra: bracket)) { 2 body { diff --git a/test/less/errors/parse-error-missing-bracket.txt b/test/less/errors/parse-error-missing-bracket.txt index a516a0acf..307bbd4aa 100644 --- a/test/less/errors/parse-error-missing-bracket.txt +++ b/test/less/errors/parse-error-missing-bracket.txt @@ -1,3 +1,3 @@ -ParseError: Missing closing '}' in {path}parse-error-missing-bracket.less on line 1, column 6: -1 body { +ParseError: Unrecognised input. Possibly missing something in {path}parse-error-missing-bracket.less on line 3, column 1: 2 background-color: #fff; +3 diff --git a/test/less/errors/parse-error-missing-parens.txt b/test/less/errors/parse-error-missing-parens.txt index f92da4588..b5998fed4 100644 --- a/test/less/errors/parse-error-missing-parens.txt +++ b/test/less/errors/parse-error-missing-parens.txt @@ -1,3 +1,3 @@ -ParseError: Missing closing ')' in {path}parse-error-missing-parens.less on line 1, column 8: +ParseError: Missing closing ')' in {path}parse-error-missing-parens.less on line 1, column 26: 1 @media (missing: bracket { 2 body { diff --git a/test/less/errors/property-asterisk-only-name.txt b/test/less/errors/property-asterisk-only-name.txt index 6adc6c696..b81d7570b 100644 --- a/test/less/errors/property-asterisk-only-name.txt +++ b/test/less/errors/property-asterisk-only-name.txt @@ -1,4 +1,4 @@ -ParseError: Unrecognised input in {path}property-asterisk-only-name.less on line 2, column 5: +ParseError: Unrecognised input in {path}property-asterisk-only-name.less on line 2, column 7: 1 a { 2 * : 1; 3 } diff --git a/test/less/errors/property-ie5-hack.txt b/test/less/errors/property-ie5-hack.txt index e42ef90eb..434943f32 100644 --- a/test/less/errors/property-ie5-hack.txt +++ b/test/less/errors/property-ie5-hack.txt @@ -1,4 +1,3 @@ -ParseError: Unrecognised input in {path}property-ie5-hack.less on line 2, column 3: -1 .test { +ParseError: Unrecognised input. Possibly missing opening '{' in {path}property-ie5-hack.less on line 3, column 1: 2 display/*/: block; /*sorry for IE5*/ 3 } From ddba05546185ca57911779eff5a2f4a28590cd86 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 14 Aug 2014 18:03:59 +0100 Subject: [PATCH 044/161] Fix merge errors and node tests --- lib/less/tree/element.js | 1 + test/css/import-inline.css | 2 +- test/sourcemaps/basic.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 3b5a6a71c..925b4b936 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -42,3 +42,4 @@ Element.prototype = { } }; return Element; +}; diff --git a/test/css/import-inline.css b/test/css/import-inline.css index 5e729d198..6e9568984 100644 --- a/test/css/import-inline.css +++ b/test/css/import-inline.css @@ -1,5 +1,5 @@ #import { - color: #ff0000; + color: red; } @media (min-width: 600px) { #css { color: yellow; } diff --git a/test/sourcemaps/basic.json b/test/sourcemaps/basic.json index e548ead74..51372f313 100644 --- a/test/sourcemaps/basic.json +++ b/test/sourcemaps/basic.json @@ -1 +1 @@ -{"version":3,"sources":["testweb/sourcemaps/basic.less","testweb/sourcemaps/imported.css"],"names":[],"mappings":"AAMA;EACE,YAAA;EAJA,UAAA;EAWA,iBAAA;EALA,WAAA;EACA,mBAAA;;AAJF,EASE;AATF,EASM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;EAGA,UAAA;;AALN;AAAI;AAUJ;EATE,iBAAA;;AADF,EAEE;AAFE,EAEF;AAFF,EAEM;AAFF,EAEE;AAQN,OARE;AAQF,OARM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAQN,OARE,GAQF,UARE;AAQF,OARE,GAEI,KAFJ;AAQF,OARE,GAQF,UARM;AAQN,OARE,GAEI,KAFA;AAEF,EAFF,GAQF,UARE;AAEE,EAFF,GAQF,UARM;AAQN,OARM,GAQN,UARE;AAQF,OARM,GAEA,KAFJ;AAQF,OARM,GAQN,UARM;AAQN,OARM,GAEA,KAFA;AAEF,EAFE,GAQN,UARE;AAEE,EAFE,GAQN,UARM;EAGA,UAAA;;AAKN;EACE,WAAA;;ACxBF;AACA;AACA;AACA;AACA;AACA;AACA","file":"sourcemaps/basic.css"} \ No newline at end of file +{"version":3,"sources":["testweb/sourcemaps/basic.less","testweb/sourcemaps/imported.css"],"names":[],"mappings":"AAMA;EACE,YAAA;EAJA,UAAA;EAWA,iBAAA;EALA,WAAA;EACA,iBAAA;;AAJF,EASE;AATF,EASM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;EAGA,UAAA;;AALN;AAAI;AAUJ;EATE,iBAAA;;AADF,EAEE;AAFE,EAEF;AAFF,EAEM;AAFF,EAEE;AAQN,OARE;AAQF,OARM;EACF,gBAAA;;AACA,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFJ;AAEE,EAFF,GAEI,KAFA;AAEF,EAFF,GAEI,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFJ;AAEE,EAFE,GAEA,KAFA;AAEF,EAFE,GAEA,KAFA;AAQN,OARE,GAQF,UARE;AAQF,OARE,GAEI,KAFJ;AAQF,OARE,GAQF,UARM;AAQN,OARE,GAEI,KAFA;AAEF,EAFF,GAQF,UARE;AAEE,EAFF,GAQF,UARM;AAQN,OARM,GAQN,UARE;AAQF,OARM,GAEA,KAFJ;AAQF,OARM,GAQN,UARM;AAQN,OARM,GAEA,KAFA;AAEF,EAFE,GAQN,UARE;AAEE,EAFE,GAQN,UARM;EAGA,UAAA;;AAKN;EACE,WAAA;;ACxBF;AACA;AACA;AACA;AACA;AACA;AACA","file":"sourcemaps/basic.css"} \ No newline at end of file From 953f70c879e89aec998f559abaf33dbb072bb86c Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 14 Aug 2014 18:08:31 +0100 Subject: [PATCH 045/161] Fix browser tests --- lib/less/source-map-output.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index aeddd3331..5d7d6a877 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -127,7 +127,7 @@ module.exports = function (less) { if (this._writeSourceMap) { this._writeSourceMap(sourceMapContent); } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); + sourceMapURL = "data:application/json;base64," + less.environment.encodeBase64(sourceMapContent); } if (sourceMapURL) { From d7e7ddba44bbb71daf7909d52b7921bde3e5bf67 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 16 Aug 2014 13:17:35 +0100 Subject: [PATCH 046/161] move functions into its own folder and give it a set interface --- lib/less/browser.js | 8 +- lib/less/functions.js | 736 ------------------------ lib/less/functions/color-blending.js | 74 +++ lib/less/functions/color.js | 272 +++++++++ lib/less/functions/data-uri.js | 69 +++ lib/less/functions/default.js | 25 + lib/less/functions/function-caller.js | 15 + lib/less/functions/function-registry.js | 18 + lib/less/functions/index.js | 18 + lib/less/functions/math.js | 42 ++ lib/less/functions/number.js | 74 +++ lib/less/functions/string.js | 30 + lib/less/functions/svg.js | 77 +++ lib/less/functions/types.js | 64 +++ lib/less/non-node-index.js | 4 +- lib/less/tree.js | 12 +- lib/less/tree/call.js | 12 +- test/less-test.js | 20 +- 18 files changed, 807 insertions(+), 763 deletions(-) delete mode 100644 lib/less/functions.js create mode 100644 lib/less/functions/color-blending.js create mode 100644 lib/less/functions/color.js create mode 100644 lib/less/functions/data-uri.js create mode 100644 lib/less/functions/default.js create mode 100644 lib/less/functions/function-caller.js create mode 100644 lib/less/functions/function-registry.js create mode 100644 lib/less/functions/index.js create mode 100644 lib/less/functions/math.js create mode 100644 lib/less/functions/number.js create mode 100644 lib/less/functions/string.js create mode 100644 lib/less/functions/svg.js create mode 100644 lib/less/functions/types.js diff --git a/lib/less/browser.js b/lib/less/browser.js index 2023c5465..aed3d9994 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -54,11 +54,7 @@ less.poll = less.poll || (isFileProtocol ? 1000 : 1500); //Setup user functions if (options.functions) { - for(var func in options.functions) { - if (options.functions.hasOwnProperty(func)) { - less.tree.functions[func] = options.functions[func]; - } - } + less.functions.functionRegistry.addMultiple(options.functions); } var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); @@ -352,7 +348,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { } less.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { - + var newFileInfo = { currentDirectory: less.environment.getPath(env, path), filename: path, diff --git a/lib/less/functions.js b/lib/less/functions.js deleted file mode 100644 index e55f6af6b..000000000 --- a/lib/less/functions.js +++ /dev/null @@ -1,736 +0,0 @@ -module.exports = function (less, tree) { - -var functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function(c) { - if ((c instanceof tree.Quoted) && - (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { - return new(tree.Color)(c.value.slice(1)); - } - if ((c instanceof tree.Color) || (c = tree.Color.fromKeyword(c.value))) { - c.keyword = undefined; - return c; - } - throw { - type: "Argument", - message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" - }; - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (!less.environment.supportsDataURI(this.env)) { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - var fragmentStart = filePath.indexOf('#'); - var fragment = ''; - if (fragmentStart!==-1) { - fragment = filePath.slice(fragmentStart); - filePath = filePath.slice(0, fragmentStart); - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = less.environment.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = less.environment.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - - mimetype = less.environment.mimeLookup(this.env, filePath); - - // use base 64 unless it's an ASCII or UTF-8 format - var charset = less.environment.charsetLookup(this.env, mimetype); - useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; - if (useBase64) { mimetype += ';base64'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = less.environment.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = less.environment.encodeBase64(this.env, returner); - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - functions[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - functions[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - functions["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo, environment) { - this.env = env; - this.environment = environment; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = functions; - -return functions; - -}; diff --git a/lib/less/functions/color-blending.js b/lib/less/functions/color-blending.js new file mode 100644 index 000000000..c24b549aa --- /dev/null +++ b/lib/less/functions/color-blending.js @@ -0,0 +1,74 @@ +module.exports = function(functions, tree) { + + // Color Blending + // ref: http://www.w3.org/TR/compositing-1 + + function colorBlend(mode, color1, color2) { + var ab = color1.alpha, cb, // backdrop + as = color2.alpha, cs, // source + ar, cr, r = []; // result + + ar = as + ab * (1 - as); + for (var i = 0; i < 3; i++) { + cb = color1.rgb[i] / 255; + cs = color2.rgb[i] / 255; + cr = mode(cb, cs); + if (ar) { + cr = (as * cs + ab * (cb + - as * (cb + cs - cr))) / ar; + } + r[i] = cr * 255; + } + + return new(tree.Color)(r, ar); + } + + var colorBlendModeFunctions = { + multiply: function(cb, cs) { + return cb * cs; + }, + screen: function(cb, cs) { + return cb + cs - cb * cs; + }, + overlay: function(cb, cs) { + cb *= 2; + return (cb <= 1) + ? colorBlendModeFunctions.multiply(cb, cs) + : colorBlendModeFunctions.screen(cb - 1, cs); + }, + softlight: function(cb, cs) { + var d = 1, e = cb; + if (cs > 0.5) { + e = 1; + d = (cb > 0.25) ? Math.sqrt(cb) + : ((16 * cb - 12) * cb + 4) * cb; + } + return cb - (1 - 2 * cs) * e * (d - cb); + }, + hardlight: function(cb, cs) { + return colorBlendModeFunctions.overlay(cs, cb); + }, + difference: function(cb, cs) { + return Math.abs(cb - cs); + }, + exclusion: function(cb, cs) { + return cb + cs - 2 * cb * cs; + }, + + // non-w3c functions: + average: function(cb, cs) { + return (cb + cs) / 2; + }, + negation: function(cb, cs) { + return 1 - Math.abs(cb + cs - 1); + } + }; + + for (var f in colorBlendModeFunctions) { + if (colorBlendModeFunctions.hasOwnProperty(f)) { + colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]); + } + } + + functions.functionRegistry.addMultiple(colorBlend); +}; diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js new file mode 100644 index 000000000..1b493bcea --- /dev/null +++ b/lib/less/functions/color.js @@ -0,0 +1,272 @@ +module.exports = function(functions, tree) { + function clamp(val) { + return Math.min(1, Math.max(0, val)); + } + function hsla(color) { + return colorFunctions.hsla(color.h, color.s, color.l, color.a); + } + function number(n) { + if (n instanceof tree.Dimension) { + return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); + } else if (typeof(n) === 'number') { + return n; + } else { + throw { + error: "RuntimeError", + message: "color functions take numbers as parameters" + }; + } + } + function scaled(n, size) { + if (n instanceof tree.Dimension && n.unit.is('%')) { + return parseFloat(n.value * size / 100); + } else { + return number(n); + } + } + var colorFunctions = { + rgb: function (r, g, b) { + return colorFunctions.rgba(r, g, b, 1.0); + }, + rgba: function (r, g, b, a) { + var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); + a = number(a); + return new(tree.Color)(rgb, a); + }, + hsl: function (h, s, l) { + return colorFunctions.hsla(h, s, l, 1.0); + }, + hsla: function (h, s, l, a) { + function hue(h) { + 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; } + } + + h = (number(h) % 360) / 360; + s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); + + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + return colorFunctions.rgba(hue(h + 1/3) * 255, + hue(h) * 255, + hue(h - 1/3) * 255, + a); + }, + + hsv: function(h, s, v) { + return colorFunctions.hsva(h, s, v, 1.0); + }, + + hsva: function(h, s, v, a) { + h = ((number(h) % 360) / 360) * 360; + s = number(s); v = number(v); a = number(a); + + var i, f; + i = Math.floor((h / 60) % 6); + f = (h / 60) - i; + + var vs = [v, + v * (1 - s), + v * (1 - f * s), + v * (1 - (1 - f) * s)]; + var perm = [[0, 3, 1], + [2, 0, 1], + [1, 0, 3], + [1, 2, 0], + [3, 1, 0], + [0, 1, 2]]; + + return colorFunctions.rgba(vs[perm[i][0]] * 255, + vs[perm[i][1]] * 255, + vs[perm[i][2]] * 255, + a); + }, + + hue: function (color) { + return new(tree.Dimension)(color.toHSL().h); + }, + saturation: function (color) { + return new(tree.Dimension)(color.toHSL().s * 100, '%'); + }, + lightness: function (color) { + return new(tree.Dimension)(color.toHSL().l * 100, '%'); + }, + hsvhue: function(color) { + return new(tree.Dimension)(color.toHSV().h); + }, + hsvsaturation: function (color) { + return new(tree.Dimension)(color.toHSV().s * 100, '%'); + }, + hsvvalue: function (color) { + return new(tree.Dimension)(color.toHSV().v * 100, '%'); + }, + red: function (color) { + return new(tree.Dimension)(color.rgb[0]); + }, + green: function (color) { + return new(tree.Dimension)(color.rgb[1]); + }, + blue: function (color) { + return new(tree.Dimension)(color.rgb[2]); + }, + alpha: function (color) { + return new(tree.Dimension)(color.toHSL().a); + }, + luma: function (color) { + return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); + }, + luminance: function (color) { + var luminance = + (0.2126 * color.rgb[0] / 255) + + (0.7152 * color.rgb[1] / 255) + + (0.0722 * color.rgb[2] / 255); + + return new(tree.Dimension)(luminance * color.alpha * 100, '%'); + }, + saturate: function (color, amount) { + // filter: saturate(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + var hsl = color.toHSL(); + + hsl.s += amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + desaturate: function (color, amount) { + var hsl = color.toHSL(); + + hsl.s -= amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + lighten: function (color, amount) { + var hsl = color.toHSL(); + + hsl.l += amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + darken: function (color, amount) { + var hsl = color.toHSL(); + + hsl.l -= amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + fadein: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a += amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fadeout: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a -= amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fade: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a = amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + spin: function (color, amount) { + var hsl = color.toHSL(); + var hue = (hsl.h + amount.value) % 360; + + hsl.h = hue < 0 ? 360 + hue : hue; + + return hsla(hsl); + }, + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + mix: function (color1, color2, weight) { + if (!weight) { + weight = new(tree.Dimension)(50); + } + var p = weight.value / 100.0; + var w = p * 2 - 1; + var a = color1.toHSL().a - color2.toHSL().a; + + var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, + color1.rgb[1] * w1 + color2.rgb[1] * w2, + color1.rgb[2] * w1 + color2.rgb[2] * w2]; + + var alpha = color1.alpha * p + color2.alpha * (1 - p); + + return new(tree.Color)(rgb, alpha); + }, + greyscale: function (color) { + return colorFunctions.desaturate(color, new(tree.Dimension)(100)); + }, + contrast: function (color, dark, light, threshold) { + // filter: contrast(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + if (typeof light === 'undefined') { + light = colorFunctions.rgba(255, 255, 255, 1.0); + } + if (typeof dark === 'undefined') { + dark = colorFunctions.rgba(0, 0, 0, 1.0); + } + //Figure out which is actually light and dark! + if (dark.luma() > light.luma()) { + var t = light; + light = dark; + dark = t; + } + if (typeof threshold === 'undefined') { + threshold = 0.43; + } else { + threshold = number(threshold); + } + if (color.luma() < threshold) { + return light; + } else { + return dark; + } + }, + argb: function (color) { + return new(tree.Anonymous)(color.toARGB()); + }, + color: function(c) { + if ((c instanceof tree.Quoted) && + (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { + return new(tree.Color)(c.value.slice(1)); + } + if ((c instanceof tree.Color) || (c = tree.Color.fromKeyword(c.value))) { + c.keyword = undefined; + return c; + } + throw { + type: "Argument", + message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" + }; + }, + tint: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount); + }, + shade: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount); + } + }; + functions.functionRegistry.addMultiple(colorFunctions); +}; diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js new file mode 100644 index 000000000..c60be3aa4 --- /dev/null +++ b/lib/less/functions/data-uri.js @@ -0,0 +1,69 @@ +module.exports = function(functions, tree, less) { + functions.functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { + + if (!less.environment.supportsDataURI(this.env)) { + return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); + } + + var mimetype = mimetypeNode.value; + var filePath = (filePathNode && filePathNode.value); + + var useBase64 = false; + + if (arguments.length < 2) { + filePath = mimetype; + } + + var fragmentStart = filePath.indexOf('#'); + var fragment = ''; + if (fragmentStart!==-1) { + fragment = filePath.slice(fragmentStart); + filePath = filePath.slice(0, fragmentStart); + } + + if (this.env.isPathRelative(filePath)) { + if (this.currentFileInfo.relativeUrls) { + filePath = less.environment.join(this.currentFileInfo.currentDirectory, filePath); + } else { + filePath = less.environment.join(this.currentFileInfo.entryPath, filePath); + } + } + + // detect the mimetype if not given + if (arguments.length < 2) { + + mimetype = less.environment.mimeLookup(this.env, filePath); + + // use base 64 unless it's an ASCII or UTF-8 format + var charset = less.environment.charsetLookup(this.env, mimetype); + useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; + if (useBase64) { mimetype += ';base64'; } + } + else { + useBase64 = /;base64$/.test(mimetype); + } + + var buf = less.environment.readFileSync(filePath); + + // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded + // and the --ieCompat flag is enabled, return a normal url() instead. + var DATA_URI_MAX_KB = 32, + fileSizeInKB = parseInt((buf.length / 1024), 10); + if (fileSizeInKB >= DATA_URI_MAX_KB) { + + if (this.env.ieCompat !== false) { + if (!this.env.silent) { + console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); + } + + return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); + } + } + + buf = useBase64 ? buf.toString('base64') + : encodeURIComponent(buf); + + var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; + return new(tree.URL)(new(tree.Anonymous)(uri)); + }); +}; diff --git a/lib/less/functions/default.js b/lib/less/functions/default.js new file mode 100644 index 000000000..6aa78aaa4 --- /dev/null +++ b/lib/less/functions/default.js @@ -0,0 +1,25 @@ +module.exports = function(functions, tree) { + var defaultFunc = { + eval: function () { + var v = this.value_, e = this.error_; + if (e) { + throw e; + } + if (v != null) { + return v ? tree.True : tree.False; + } + }, + value: function (v) { + this.value_ = v; + }, + error: function (e) { + this.error_ = e; + }, + reset: function () { + this.value_ = this.error_ = null; + } + }; + + functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); + tree.defaultFunc = defaultFunc; +}; diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js new file mode 100644 index 000000000..da86d513d --- /dev/null +++ b/lib/less/functions/function-caller.js @@ -0,0 +1,15 @@ +module.exports = function(functions) { +var functionCaller = function(name, env, currentFileInfo) { + this.name = name.toLowerCase(); + this.function = functions.functionRegistry.get(this.name); + this.env = env; + this.currentFileInfo = currentFileInfo; +}; +functionCaller.prototype.isValid = function() { + return Boolean(this.function); +}; +functionCaller.prototype.call = function(args) { + return this.function.apply(this, args); +}; +return functionCaller; +}; diff --git a/lib/less/functions/function-registry.js b/lib/less/functions/function-registry.js new file mode 100644 index 000000000..d39230d1d --- /dev/null +++ b/lib/less/functions/function-registry.js @@ -0,0 +1,18 @@ +module.exports = { + _data: {}, + add: function(name, func) { + if (this._data.hasOwnProperty(name)) { + //TODO warn + } + this._data[name] = func; + }, + addMultiple: function(functions) { + Object.keys(functions).forEach( + function(name) { + this.add(name, functions[name]); + }.bind(this)); + }, + get: function(name) { + return this._data[name]; + } +}; diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js new file mode 100644 index 000000000..36d716760 --- /dev/null +++ b/lib/less/functions/index.js @@ -0,0 +1,18 @@ +module.exports = function(less, tree) { + var functions = {}; + functions.functionRegistry = require("./function-registry.js"); + functions.functionCaller = require("./function-caller.js")(functions); + + //register functions + require("./color.js")(functions, tree); + require("./color-blending.js")(functions, tree); + require("./data-uri.js")(functions, tree, less); + require("./default.js")(functions, tree); + require("./math.js")(functions, tree); + require("./number.js")(functions, tree); + require("./string.js")(functions, tree); + require("./svg.js")(functions, tree, less); + require("./types.js")(functions, tree); + + return functions; +}; diff --git a/lib/less/functions/math.js b/lib/less/functions/math.js new file mode 100644 index 000000000..4e56d8d2a --- /dev/null +++ b/lib/less/functions/math.js @@ -0,0 +1,42 @@ +module.exports = function(functions, tree) { + + var mathFunctions = { + // name, unit + ceil: null, + floor: null, + sqrt: null, + abs: null, + tan: "", + sin: "", + cos: "", + atan: "rad", + asin: "rad", + acos: "rad" + }; + + function _math(fn, unit, n) { + if (!(n instanceof tree.Dimension)) { + throw { type: "Argument", message: "argument must be a number" }; + } + if (unit == null) { + unit = n.unit; + } else { + n = n.unify(); + } + return new(tree.Dimension)(fn(parseFloat(n.value)), unit); + } + + for (var f in mathFunctions) { + if (mathFunctions.hasOwnProperty(f)) { + mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]); + } + } + + mathFunctions.round = function (n, f) { + var fraction = typeof(f) === "undefined" ? 0 : f.value; + return _math(function(num) { return num.toFixed(fraction); }, null, n); + }; + + functions.functionRegistry.addMultiple(mathFunctions); + +}; diff --git a/lib/less/functions/number.js b/lib/less/functions/number.js new file mode 100644 index 000000000..dc6fdfd59 --- /dev/null +++ b/lib/less/functions/number.js @@ -0,0 +1,74 @@ +module.exports = function(functions, tree) { + var minMax = function (isMin, args) { + args = Array.prototype.slice.call(args); + switch(args.length) { + case 0: throw { type: "Argument", message: "one or more arguments required" }; + } + var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, + order = [], // elems only contains original argument values. + values = {}; // key is the unit.toString() for unified tree.Dimension values, + // value is the index into the order array. + for (i = 0; i < args.length; i++) { + current = args[i]; + if (!(current instanceof tree.Dimension)) { + if(Array.isArray(args[i].value)) { + Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); + } + continue; + } + currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); + unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); + unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; + unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; + j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; + if (j === undefined) { + if(unitStatic !== undefined && unit !== unitStatic) { + throw{ type: "Argument", message: "incompatible types" }; + } + values[unit] = order.length; + order.push(current); + continue; + } + referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); + if ( isMin && currentUnified.value < referenceUnified.value || + !isMin && currentUnified.value > referenceUnified.value) { + order[j] = current; + } + } + if (order.length == 1) { + return order[0]; + } + args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); + return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); + }; + functions.functionRegistry.addMultiple({ + min: function () { + return minMax(true, arguments); + }, + max: function () { + return minMax(false, arguments); + }, + convert: function (val, unit) { + return val.convertTo(unit.value); + }, + pi: function () { + return new(tree.Dimension)(Math.PI); + }, + mod: function(a, b) { + return new(tree.Dimension)(a.value % b.value, a.unit); + }, + pow: function(x, y) { + if (typeof x === "number" && typeof y === "number") { + x = new(tree.Dimension)(x); + y = new(tree.Dimension)(y); + } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { + throw { type: "Argument", message: "arguments must be numbers" }; + } + + return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); + }, + percentage: function (n) { + return new(tree.Dimension)(n.value * 100, '%'); + } + }); +}; diff --git a/lib/less/functions/string.js b/lib/less/functions/string.js new file mode 100644 index 000000000..9851c8918 --- /dev/null +++ b/lib/less/functions/string.js @@ -0,0 +1,30 @@ +module.exports = function(functions, tree) { + functions.functionRegistry.addMultiple({ + e: function (str) { + return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); + }, + escape: function (str) { + return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); + }, + replace: function (string, pattern, replacement, flags) { + var result = string.value; + + result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); + return new(tree.Quoted)(string.quote || '', result, string.escaped); + }, + '%': function (string /* arg, arg, ...*/) { + var args = Array.prototype.slice.call(arguments, 1), + result = string.value; + + for (var i = 0; i < args.length; i++) { + /*jshint loopfunc:true */ + result = result.replace(/%[sda]/i, function(token) { + var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); + return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; + }); + } + result = result.replace(/%%/g, '%'); + return new(tree.Quoted)(string.quote || '', result, string.escaped); + } + }); +}; diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js new file mode 100644 index 000000000..0cd71d571 --- /dev/null +++ b/lib/less/functions/svg.js @@ -0,0 +1,77 @@ +module.exports = function(functions, tree, less) { + functions.functionRegistry.add("svg-gradient", function(direction) { + + function throwArgumentDescriptor() { + throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; + } + + if (arguments.length < 3) { + throwArgumentDescriptor(); + } + var stops = Array.prototype.slice.call(arguments, 1), + gradientDirectionSvg, + gradientType = "linear", + rectangleDimension = 'x="0" y="0" width="1" height="1"', + useBase64 = true, + renderEnv = {compress: false}, + returner, + directionValue = direction.toCSS(renderEnv), + i, color, position, positionValue, alpha; + + 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 { type: "Argument", message: "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 < stops.length; i+= 1) { + if (stops[i].value) { + color = stops[i].value[0]; + position = stops[i].value[1]; + } else { + color = stops[i]; + position = undefined; + } + + if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { + throwArgumentDescriptor(); + } + positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; + alpha = color.alpha; + returner += ''; + } + returner += '' + + ''; + + if (useBase64) { + try { + returner = less.environment.encodeBase64(this.env, returner); + } catch(e) { + useBase64 = false; + } + } + + returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; + return new(tree.URL)(new(tree.Anonymous)(returner)); + }); +}; diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js new file mode 100644 index 000000000..802d8094b --- /dev/null +++ b/lib/less/functions/types.js @@ -0,0 +1,64 @@ +module.exports = function(functions, tree) { + var isa = function (n, Type) { + return (n instanceof Type) ? tree.True : tree.False; + }, + isunit = function (n, unit) { + return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; + }; + functions.functionRegistry.addMultiple({ + iscolor: function (n) { + return isa(n, tree.Color); + }, + isnumber: function (n) { + return isa(n, tree.Dimension); + }, + isstring: function (n) { + return isa(n, tree.Quoted); + }, + iskeyword: function (n) { + return isa(n, tree.Keyword); + }, + isurl: function (n) { + return isa(n, tree.URL); + }, + ispixel: function (n) { + return isunit(n, 'px'); + }, + ispercentage: function (n) { + return isunit(n, '%'); + }, + isem: function (n) { + return isunit(n, 'em'); + }, + isunit: isunit, + unit: function (val, unit) { + if(!(val instanceof tree.Dimension)) { + throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; + } + if (unit) { + if (unit instanceof tree.Keyword) { + unit = unit.value; + } else { + unit = unit.toCSS(); + } + } else { + unit = ""; + } + return new(tree.Dimension)(val.value, unit); + }, + "get-unit": function (n) { + return new(tree.Anonymous)(n.unit); + }, + extract: function(values, index) { + index = index.value - 1; // (1-based index) + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + return Array.isArray(values.value) ? + values.value[index] : Array(values)[index]; + }, + length: function(values) { + var n = Array.isArray(values.value) ? values.value.length : 1; + return new tree.Dimension(n); + } + }); +}; diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index cacb207c7..c93846df4 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -5,10 +5,10 @@ var less = { } }; -less.tree = (require('./tree'))(less); +less.tree = (require('./tree'))(less, less.data); less.visitor = require('./visitor/index.js')(less, less.tree); less.Parser = (require('./parser'))(less, less.tree, less.visitor); -less.tree.functions = (require('./functions'))(less, less.tree); +less.functions = (require('./functions/index.js'))(less, less.tree); require('./env')(less.tree); less.tree.sourceMapOutput = require('./source-map-output.js')(less); diff --git a/lib/less/tree.js b/lib/less/tree.js index d771e3c48..b3d7b1c59 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -1,4 +1,4 @@ -module.exports = function (less) { +module.exports = function (less, data) { var tree = {}; @@ -97,7 +97,7 @@ tree.outputRuleset = function (env, output, rules) { }; tree.Alpha = require('./tree/alpha')(tree); -tree.Color = require('./tree/color')(less.data, tree); +tree.Color = require('./tree/color')(data, tree); tree.Directive = require('./tree/directive')(tree); tree.DetachedRuleset = require('./tree/detached-ruleset')(tree); tree.Operation = require('./tree/operation')(tree); @@ -113,7 +113,7 @@ tree.Selector = require('./tree/selector')(tree); tree.Quoted = require('./tree/quoted')(tree); tree.Expression = require('./tree/expression')(tree); tree.Rule = require('./tree/rule')(tree); -tree.Call = require('./tree/call')(tree); +tree.Call = require('./tree/call')(tree, less); tree.URL = require('./tree/url')(tree); tree.Import = require('./tree/import')(tree); tree.mixin = { @@ -133,6 +133,12 @@ tree.Negative = require('./tree/negative')(tree); tree.Extend = require('./tree/extend')(tree); tree.RulesetCall = require('./tree/ruleset-call')(tree); +tree.fround = function(env, value) { + var precision = env && env.numPrecision; + //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... + return (precision == null) ? value : Number((value + 2e-16).toFixed(precision)); +}; + return tree; }; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 951a0832a..08dc98986 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +module.exports = function (tree, less) { // // A function call node. @@ -18,7 +18,7 @@ Call.prototype = { }, // // When evaluating a function call, - // we either find the function in `tree.functions` [1], + // we either find the function in `less.functions` [1], // in which case we call it, passing the evaluated arguments, // if this returns null or we cannot find the function, we // simply print it out as it appeared originally [2]. @@ -31,13 +31,11 @@ Call.prototype = { // eval: function (env) { var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; + result, funcCaller = new less.functions.functionCaller(this.name, env, this.currentFileInfo); - if (nameLC in tree.functions) { // 1. + if (funcCaller.isValid()) { // 1. try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); + result = funcCaller.call(args); if (result != null) { return result; } diff --git a/test/less-test.js b/test/less-test.js index 4bba05b24..ad046d1be 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -18,15 +18,17 @@ module.exports = function() { passedTests = 0; - less.tree.functions.add = function (a, b) { - return new(less.tree.Dimension)(a.value + b.value); - }; - less.tree.functions.increment = function (a) { - return new(less.tree.Dimension)(a.value + 1); - }; - less.tree.functions._color = function (str) { - if (str.value === "evil red") { return new(less.tree.Color)("600"); } - }; + less.functions.functionRegistry.addMultiple({ + add: function (a, b) { + return new(less.tree.Dimension)(a.value + b.value); + }, + increment: function (a) { + return new(less.tree.Dimension)(a.value + 1); + }, + _color: function (str) { + if (str.value === "evil red") { return new(less.tree.Color)("600"); } + } + }); function testSourcemap(name, err, compiledLess, doReplacements, sourcemap) { fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) { From cde9b79b6c424590fe1238418edb45c57d6dea69 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 17 Aug 2014 11:30:18 +0100 Subject: [PATCH 047/161] much improved comment parsing --- lib/less/parser.js | 113 ++++++++++++++-------- lib/less/tree/comment.js | 10 +- test/css/comments.css | 4 + test/css/comments2.css | 12 +++ test/css/debug/linenumbers-all.css | 1 + test/css/debug/linenumbers-comments.css | 1 + test/css/debug/linenumbers-mediaquery.css | 1 + test/less-test.js | 26 ++--- test/less/comments.less | 5 + test/less/comments2.less | 20 ++++ test/less/errors/comment-in-selector.less | 1 - test/less/errors/comment-in-selector.txt | 2 - 12 files changed, 132 insertions(+), 64 deletions(-) create mode 100644 test/css/comments2.css create mode 100644 test/less/comments2.less delete mode 100644 test/less/errors/comment-in-selector.less delete mode 100644 test/less/errors/comment-in-selector.txt diff --git a/lib/less/parser.js b/lib/less/parser.js index b7d4a3329..05e0d6c36 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -156,7 +156,7 @@ var Parser = function Parser(env) { } function isWhitespace(str, pos) { var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); + return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF); } // // Parse from a token, regexp or string, and move forward if match @@ -228,21 +228,56 @@ var Parser = function Parser(env) { return tok; } + var CHARCODE_SPACE = 32, + CHARCODE_TAB = 9, + CHARCODE_LF = 10, + CHARCODE_CR = 13, + CHARCODE_FORWARD_SLASH = 47; + + var autoCommentAbsorb = true, + commentStore = []; + function skipWhitespace(length) { var oldi = i, oldj = j, curr = i - currentPos, endIndex = i + current.length - curr, mem = (i += length), inp = input, - c; + c, nextChar, comment; for (; i < endIndex; i++) { c = inp.charCodeAt(i); - if (c > 32) { + + if (autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) { + nextChar = inp[i + 1]; + if (nextChar === '/') { + comment = {index: i, isLineComment: true}; + var nextNewLine = inp.indexOf("\n", i + 1); + if (nextNewLine < 0) { + nextNewLine = endIndex; + } + i = nextNewLine; + comment.text = inp.substr(comment.i, i - comment.i); + commentStore.push(comment); + continue; + } else if (nextChar === '*') { + var haystack = inp.substr(i); + var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//); + if (comment_search_result) { + comment = { + index: i, + text: comment_search_result[0], + isLineComment: false + }; + i += comment.text.length - 1; + commentStore.push(comment); + continue; + } + } break; } - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { + if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) { break; } } @@ -403,6 +438,7 @@ var Parser = function Parser(env) { // with the `root` property set to true, so no `{}` are // output. The callback is called when the input is parsed. try { + skipWhitespace(0); root = new(tree.Ruleset)(null, this.parsers.primary()); root.root = true; root.firstRoot = true; @@ -530,7 +566,7 @@ var Parser = function Parser(env) { // string, so we've got a parsing error. // // We try to extract a \n delimited string, - // showing the line where the parse error occured. + // showing the line where the parse error occurred. // We split it up into two parts (the part which parsed, // and the part which didn't), so we can color them differently. if (i < input.length - 1) { @@ -643,8 +679,16 @@ var Parser = function Parser(env) { while (current) { + while(true) { + node = this.comment(); + if (!node) { break; } + root.push(node); + } + if (peekChar('}')) { + break; + } node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); + mixin.call() || this.rulesetCall() || this.directive(); if (node) { root.push(node); } else { @@ -652,43 +696,18 @@ var Parser = function Parser(env) { break; } } - if (peekChar('}')) { - break; - } } return root; }, - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. + // comments are collected by the main parsing mechanism and then assigned to nodes + // where the current structure allows it comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); + if (commentStore.length) { + var comment = commentStore.shift(); + return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, env.currentFileInfo); } - - return comments; }, // @@ -824,9 +843,13 @@ var Parser = function Parser(env) { return; } + autoCommentAbsorb = false; + value = this.quoted() || this.variable() || $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; + autoCommentAbsorb = true; + expectChar(')'); return new(tree.URL)((value.value != null || value instanceof tree.Variable) @@ -1062,7 +1085,7 @@ var Parser = function Parser(env) { if (isCall) { arg = parsers.detachedRuleset() || parsers.expression(); } else { - parsers.comments(); + commentStore.length = 0; if (input.charAt(i) === '.' && $re(/^\.{3}/)) { returner.variadic = true; if ($char(";") && !isSemiColonSeperated) { @@ -1212,7 +1235,7 @@ var Parser = function Parser(env) { return; } - parsers.comments(); + commentStore.length = 0; if ($re(/^when/)) { // Guard cond = expect(parsers.conditions, 'expected condition'); @@ -1239,9 +1262,8 @@ var Parser = function Parser(env) { entity: function () { var entities = this.entities; - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); + return this.comment() || entities.literal() || entities.variable() || entities.url() || + entities.call() || entities.keyword() || entities.javascript(); }, // @@ -1447,7 +1469,7 @@ var Parser = function Parser(env) { break; } if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); + commentStore.length = 0; if (s.condition && selectors.length > 1) { error("Guards are only currently allowed on a single selector."); } @@ -1455,7 +1477,7 @@ var Parser = function Parser(env) { if (s.condition) { error("Guards are only currently allowed on a single selector."); } - this.comments(); + commentStore.length = 0; } if (selectors && (rules = this.block())) { @@ -1930,6 +1952,11 @@ var Parser = function Parser(env) { var entities = [], e, delim; do { + e = this.comment(); + if (e) { + entities.push(e); + continue; + } e = this.addition() || this.entity(); if (e) { entities.push(e); diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index 05bc2207a..10c1e794d 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -1,8 +1,8 @@ module.exports = function (tree) { -var Comment = function (value, silent, index, currentFileInfo) { +var Comment = function (value, isLineComment, index, currentFileInfo) { this.value = value; - this.silent = !!silent; + this.isLineComment = isLineComment; this.currentFileInfo = currentFileInfo; }; Comment.prototype = { @@ -11,13 +11,13 @@ Comment.prototype = { if (this.debugInfo) { output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n + output.add(this.value); }, toCSS: tree.toCSS, isSilent: function(env) { var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; + isCompressed = env.compress && this.value[2] !== "!"; + return this.isLineComment || isReference || isCompressed; }, eval: function () { return this; }, markReferenced: function () { diff --git a/test/css/comments.css b/test/css/comments.css index 3d86fb77a..f79b75974 100644 --- a/test/css/comments.css +++ b/test/css/comments.css @@ -67,3 +67,7 @@ color: #A33; } /* } */ +/*by block */ +#output-block { + comment: /* // Not commented out // */; +} diff --git a/test/css/comments2.css b/test/css/comments2.css new file mode 100644 index 000000000..193265585 --- /dev/null +++ b/test/css/comments2.css @@ -0,0 +1,12 @@ +@-webkit-keyframes hover { + /* Safari and Chrome */ +} +.bg { + background-image: linear-gradient(#333333 /*{comment}*/, #111111); +} +#planadvisor, +.first, +.planning { + margin: 10px; + total-width: (1 * 6em * 12) + (2em * 12); +} diff --git a/test/css/debug/linenumbers-all.css b/test/css/debug/linenumbers-all.css index 87022aecc..eabeb41fd 100644 --- a/test/css/debug/linenumbers-all.css +++ b/test/css/debug/linenumbers-all.css @@ -2,6 +2,7 @@ /* line 1, {pathimport}test.less */ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000031}} /* @charset "ISO-8859-1"; */ + /* line 23, {pathimport}test.less */ @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}} .tst3 { diff --git a/test/css/debug/linenumbers-comments.css b/test/css/debug/linenumbers-comments.css index e5d6bb385..a00fd7ffb 100644 --- a/test/css/debug/linenumbers-comments.css +++ b/test/css/debug/linenumbers-comments.css @@ -1,6 +1,7 @@ @charset "UTF-8"; /* line 1, {pathimport}test.less */ /* @charset "ISO-8859-1"; */ + /* line 23, {pathimport}test.less */ .tst3 { color: grey; diff --git a/test/css/debug/linenumbers-mediaquery.css b/test/css/debug/linenumbers-mediaquery.css index e252ab3c2..11cdd24e6 100644 --- a/test/css/debug/linenumbers-mediaquery.css +++ b/test/css/debug/linenumbers-mediaquery.css @@ -1,6 +1,7 @@ @charset "UTF-8"; @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\000031}} /* @charset "ISO-8859-1"; */ + @media -sass-debug-info{filename{font-family:file\:\/\/{pathimportesc}test\.less}line{font-family:\0000323}} .tst3 { color: grey; diff --git a/test/less-test.js b/test/less-test.js index ad046d1be..22e45a0b8 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -38,8 +38,8 @@ module.exports = function() { } else if (err) { fail("ERROR: " + (err && err.message)); if (isVerbose) { - console.error(); - console.error(err.stack); + process.stdout.write("\n"); + process.stdout.write(err.stack + "\n"); } } else { difference("FAIL", expectedSourcemap, sourcemap); @@ -138,8 +138,8 @@ module.exports = function() { else if (err) { fail("ERROR: " + (err && err.message)); if (isVerbose) { - console.error(); - console.error(err.stack); + process.stdout.write("\n"); + process.stdout.write(err.stack + "\n"); } } else { difference("FAIL", css, less); @@ -158,17 +158,17 @@ module.exports = function() { process.stdout.write(item.value); } }); - console.log(""); + process.stdout.write("\n"); } function fail(msg) { - console.error(stylize(msg, 'red')); + process.stdout.write(stylize(msg, 'red') + "\n"); failedTests++; endTest(); } function difference(msg, left, right) { - console.warn(stylize(msg, 'yellow')); + process.stdout.write(stylize(msg, 'yellow') + "\n"); failedTests++; diff(left, right); @@ -176,7 +176,7 @@ module.exports = function() { } function ok(msg) { - console.log(stylize(msg, 'green')); + process.stdout.write(stylize(msg, 'green') + "\n"); passedTests++; endTest(); } @@ -184,15 +184,15 @@ module.exports = function() { function endTest() { var leaked = checkGlobalLeaks(); if (failedTests + passedTests === totalTests) { - console.log(""); + process.stdout.write("\n"); if (failedTests > 0) { - console.error(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed"); + process.stdout.write(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed\n"); } else { - console.log(stylize("All Passed ", "green") + passedTests + " run"); + process.stdout.write(stylize("All Passed ", "green") + passedTests + " run\n"); } if (leaked.length > 0) { - console.log(""); - console.warn(stylize("Global leak detected: ", "red") + leaked.join(', ')); + process.stdout.write("\n"); + process.stdout.write(stylize("Global leak detected: ", "red") + leaked.join(', ') + "\n"); } if (leaked.length || failedTests) { diff --git a/test/less/comments.less b/test/less/comments.less index e2569b670..34b3dbbf2 100644 --- a/test/less/comments.less +++ b/test/less/comments.less @@ -81,3 +81,8 @@ // /* *//* { *//* *//* *//* */#div { color:#A33; }/* } */ + +// line immediatly followed +/*by block */ +@string_w_comment: ~"/* // Not commented out // */"; +#output-block { comment: @string_w_comment; } diff --git a/test/less/comments2.less b/test/less/comments2.less new file mode 100644 index 000000000..19ef5d7eb --- /dev/null +++ b/test/less/comments2.less @@ -0,0 +1,20 @@ +@media all and/*! */(max-width:1024px) {} +@-webkit-keyframes hover /* Safari and Chrome */{ } +.bg { + background-image: linear-gradient(#333 /*{comment}*/, #111); +} +#planadvisor, +/*comment*//*comment*/ +.first,/*comment*//*comment*/.planning { + margin:10px; + total-width: @total-width; +} +@base : 1; +@column-width : @base * 6em; // Width of column */ +@gutter-width : 2em; // Width of column spacing */ +@columns : 12; // Number of Columns */ +@gridsystem-width : (@column-width * // For calculating the total */ + @columns) + ( // width of the content area. */ + @gutter-width * // We strongly recommend you */ + @columns); // do not change this formula. */ +@total-width : @gridsystem-width; // set to 100% for fluid grid */ diff --git a/test/less/errors/comment-in-selector.less b/test/less/errors/comment-in-selector.less deleted file mode 100644 index a7d263965..000000000 --- a/test/less/errors/comment-in-selector.less +++ /dev/null @@ -1 +0,0 @@ -#gaga /* Comment */ span { color: red } \ No newline at end of file diff --git a/test/less/errors/comment-in-selector.txt b/test/less/errors/comment-in-selector.txt deleted file mode 100644 index e48f878ca..000000000 --- a/test/less/errors/comment-in-selector.txt +++ /dev/null @@ -1,2 +0,0 @@ -ParseError: Unrecognised input in {path}comment-in-selector.less on line 1, column 21: -1 #gaga /* Comment */ span { color: red } From 119c930fa67e4ed0d1c10571798c1aa0d7e6e117 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 17 Aug 2014 11:42:28 +0100 Subject: [PATCH 048/161] remove more dist files merged from master --- dist/less-1.7.2.js | 7938 ------------------------------- dist/less-1.7.2.min.js | 16 - dist/less-1.7.3.js | 7942 ------------------------------- dist/less-1.7.3.min.js | 16 - dist/less-1.7.4.js | 7979 ------------------------------- dist/less-1.7.4.min.js | 16 - dist/less-rhino-1.7.2.js | 9313 ------------------------------------ dist/less-rhino-1.7.3.js | 9317 ------------------------------------ dist/less-rhino-1.7.4.js | 9354 ------------------------------------- dist/lessc-rhino-1.7.2.js | 449 -- dist/lessc-rhino-1.7.3.js | 449 -- dist/lessc-rhino-1.7.4.js | 449 -- 12 files changed, 53238 deletions(-) delete mode 100644 dist/less-1.7.2.js delete mode 100644 dist/less-1.7.2.min.js delete mode 100644 dist/less-1.7.3.js delete mode 100644 dist/less-1.7.3.min.js delete mode 100644 dist/less-1.7.4.js delete mode 100644 dist/less-1.7.4.min.js delete mode 100644 dist/less-rhino-1.7.2.js delete mode 100644 dist/less-rhino-1.7.3.js delete mode 100644 dist/less-rhino-1.7.4.js delete mode 100644 dist/lessc-rhino-1.7.2.js delete mode 100644 dist/lessc-rhino-1.7.3.js delete mode 100644 dist/lessc-rhino-1.7.4.js diff --git a/dist/less-1.7.2.js b/dist/less-1.7.2.js deleted file mode 100644 index 163121377..000000000 --- a/dist/less-1.7.2.js +++ /dev/null @@ -1,7938 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.2 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (!css.styleSheet) { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // For IE. - // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. - // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head - if (css.styleSheet) { - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; - } - return styles; -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.7.2.min.js b/dist/less-1.7.2.min.js deleted file mode 100644 index dc373a313..000000000 --- a/dist/less-1.7.2.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.2 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet||(j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue);var k=document.getElementsByTagName("head")[0];if(null===h||i===!1){var l=b&&b.nextSibling||null;l?l.parentNode.insertBefore(j,l):k.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),j.styleSheet)try{j.styleSheet.cssText=a}catch(m){throw new Error("Couldn't reassign styleSheet.cssText.")}if(c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(m){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;dD&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,g=y;d();var h=j(/^@import?\s+/),i=(h?this.importOptions():null)||{};return h&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),k(";"))?(f(),c=c&&new x.Value(c),new x.Import(b,c,i,g,a.currentFileInfo)):void e()},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,d,e;if(a=this.operand()){for(e=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(c=k("/")||k("*"),!c)break;if(b=this.operand(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(y+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=C,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(Math.round(a.toHSL().h))},saturation:function(a){return new d.Dimension(Math.round(100*a.toHSL().s),"%")},lightness:function(a){return new d.Dimension(Math.round(100*a.toHSL().l),"%")},hsvhue:function(a){return new d.Dimension(Math.round(a.toHSV().h))},hsvsaturation:function(a){return new d.Dimension(Math.round(100*a.toHSV().s),"%")},hsvvalue:function(a){return new d.Dimension(Math.round(100*a.toHSV().v),"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(Math.round(a.luma()*a.alpha*100),"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(Math.round(b*a.alpha*100),"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("./fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS()}).join(", ")+"]":a.toCSS()},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)t.value(h),s[h]=d.matchCondition(e,b);(s[0]||s[1])&&(s[0]!=s[1]&&(l.group=s[1]?v:w),r.push(l))}else r.push(l);q=!0}}for(t.reset(),n=[0,0,0],g=0;g0)m=w;else if(m=v,n[v]+n[w]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gh;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;ii;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames?this.frames.concat(c.frames):c.frames),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b,c;return"Quoted"!==a.type||this.escaped||a.escaped?(b=this.toCSS(),c=a.toCSS()):(b=a.value,c=this.value),b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value||c instanceof a.Ruleset?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d,e=!1,f=this.name;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:b(c,f)),"font"!==f||c.strictMath||(e=!0,c.strictMath=!0);try{if(d=this.value.eval(c),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(g){throw"number"!=typeof g.index&&(g.index=this.index,g.filename=this.currentFileInfo.filename),g}finally{e&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;fb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),h.importMultiple||(b.skip=g?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};if(d){b.root=d,b.importedFilename=i;var k=g||i in e.recursionDetector;if(!f&&(h.importMultiple||!k))return e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,j,h,e.onceFileDetectionMap,e.recursionDetector).run(d)}j()})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++); -return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var e,f=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?e=this._sourceMapURL:this._sourceMapFilename&&(e=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(f):e="data:application/json;base64,"+c("./encoder.js").encodeBase64(f),e&&this._css.push("/*# sourceMappingURL="+e+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - - save(); - - op = $char('/') || $char('*'); - - if (!op) { forget(); break; } - - a = this.operand(); - - if (!a) { restore(); break; } - forget(); - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (!css.styleSheet) { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // For IE. - // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. - // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head - if (css.styleSheet) { - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; - } - return styles; -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.7.3.min.js b/dist/less-1.7.3.min.js deleted file mode 100644 index 049efeac4..000000000 --- a/dist/less-1.7.3.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.3 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet||(j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue);var k=document.getElementsByTagName("head")[0];if(null===h||i===!1){var l=b&&b.nextSibling||null;l?l.parentNode.insertBefore(j,l):k.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),j.styleSheet)try{j.styleSheet.cssText=a}catch(m){throw new Error("Couldn't reassign styleSheet.cssText.")}if(c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(m){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([a-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;dD&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,g=y;d();var h=j(/^@import?\s+/),i=(h?this.importOptions():null)||{};return h&&(b=this.entities.quoted()||this.entities.url())&&(c=this.mediaFeatures(),k(";"))?(f(),c=c&&new x.Value(c),new x.Import(b,c,i,g,a.currentFileInfo)):void e()},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,g,i;if(a=this.operand()){for(i=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(d(),c=k("/")||k("*"),!c){f();break}if(b=this.operand(),!b){e();break}f(),a.parensInOp=!0,b.parensInOp=!0,g=new x.Operation(c,[g||a,b],i),i=h(v,y-1)}return g||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(y+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=C,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(a.toHSL().h)},saturation:function(a){return new d.Dimension(100*a.toHSL().s,"%")},lightness:function(a){return new d.Dimension(100*a.toHSL().l,"%")},hsvhue:function(a){return new d.Dimension(a.toHSV().h)},hsvsaturation:function(a){return new d.Dimension(100*a.toHSV().s,"%")},hsvvalue:function(a){return new d.Dimension(100*a.toHSV().v,"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(a.luma()*a.alpha*100,"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(b*a.alpha*100,"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("./fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS()}).join(", ")+"]":a.toCSS()},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d){this.value=a,this.index=b,this.mapLines=d,this.currentFileInfo=c},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)t.value(h),s[h]=d.matchCondition(e,b);(s[0]||s[1])&&(s[0]!=s[1]&&(l.group=s[1]?v:w),r.push(l))}else r.push(l);q=!0}}for(t.reset(),n=[0,0,0],g=0;g0)m=w;else if(m=v,n[v]+n[w]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gh;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;ii;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames?this.frames.concat(c.frames):c.frames),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b,c;return"Quoted"!==a.type||this.escaped||a.escaped?(b=this.toCSS(),c=a.toCSS()):(b=a.value,c=this.value),b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value||c instanceof a.Ruleset?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d,e=!1,f=this.name;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:b(c,f)),"font"!==f||c.strictMath||(e=!0,c.strictMath=!0);try{if(d=this.value.eval(c),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(g){throw"number"!=typeof g.index&&(g.index=this.index,g.filename=this.currentFileInfo.filename),g}finally{e&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;fb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){var d,e,f,g,h,i,j=[],k=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var l,m=b.compress?"":Array(b.tabLevel+1).join(" "),n=b.compress?"":Array(b.tabLevel).join(" ");for(d=0;dd;d++)if(i=p[d],o=i.length)for(d>0&&c.add(l),b.firstSelector=!0,i[0].genCSS(b,c),b.firstSelector=!1,e=1;o>e;e++)i[e].genCSS(b,c);c.add((b.compress?"{":" {\n")+m)}for(d=0;dd;d++)l&&c.add(l),k[d].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename),h.importMultiple||(b.skip=g?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var j=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};if(d){b.root=d,b.importedFilename=i;var k=g||i in e.recursionDetector;if(!f&&(h.importMultiple||!k))return e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,j,h,e.onceFileDetectionMap,e.recursionDetector).run(d)}j()})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++); -return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var e,f=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?e=this._sourceMapURL:this._sourceMapFilename&&(e=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(f):e="data:application/json;base64,"+c("./encoder.js").encodeBase64(f),e&&this._css.push("/*# sourceMappingURL="+e+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg, index) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - if (!elements) - error("Missing target selector for :extend()."); - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(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. - // - "import": function () { - var path, features, index = i; - - var dir = $re(/^@import?\s+/); - - if (dir) { - var options = (dir ? this.importOptions() : null) || {}; - - if ((path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - - if (!$(';')) { - i = index; - error("missing semi-colon or unrecognised media features on import"); - } - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - else - { - i = index; - error("malformed import statement"); - } - } - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - - save(); - - op = $char('/') || $char('*'); - - if (!op) { forget(); break; } - - a = this.operand(); - - if (!a) { restore(); break; } - forget(); - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; - this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - isRulesetLike: function() { - return this.rulesetLike; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - isRulesetLike: function() { - return "@charset" !== this.name; - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - function isRulesetLikeNode(rule, root) { - // if it has nested rules, then it should be treated like a ruleset - if (rule.rules) - return true; - - // medias and comments do not have nested rules, but should be treated like rulesets anyway - if ( (rule instanceof tree.Media) || (root && rule instanceof tree.Comment)) - return true; - - // some directives and anonumoust nodes are ruleset like, others are not - if ((rule instanceof tree.Directive) || (rule instanceof tree.Anonymous)) { - return rule.isRulesetLike(); - } - - //anything else is assumed to be a rule - return false; - } - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (isRulesetLikeNode(rule, this.root)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { - e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; - } - - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - if (!env.importMultiple) { - if (duplicateImport) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); - -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - if (directiveNode.rules && directiveNode.rules.rules) { - this._mergeRules(directiveNode.rules.rules); - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (!css.styleSheet) { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // For IE. - // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. - // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head - if (css.styleSheet) { - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; - } - return styles; -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([A-Za-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.7.4.min.js b/dist/less-1.7.4.min.js deleted file mode 100644 index 963d7f9c2..000000000 --- a/dist/less-1.7.4.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.4 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet||(j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue);var k=document.getElementsByTagName("head")[0];if(null===h||i===!1){var l=b&&b.nextSibling||null;l?l.parentNode.insertBefore(j,l):k.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),j.styleSheet)try{j.styleSheet.cssText=a}catch(m){throw new Error("Couldn't reassign styleSheet.cssText.")}if(c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(m){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([A-Za-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;dD&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],b||o("Missing target selector for :extend()."),f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,d=y,e=j(/^@import?\s+/);if(e){var f=(e?this.importOptions():null)||{};if(b=this.entities.quoted()||this.entities.url())return c=this.mediaFeatures(),i(";")||(y=d,o("missing semi-colon or unrecognised media features on import")),c=c&&new x.Value(c),new x.Import(b,c,f,d,a.currentFileInfo);y=d,o("malformed import statement")}},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,g,i;if(a=this.operand()){for(i=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(d(),c=k("/")||k("*"),!c){f();break}if(b=this.operand(),!b){e();break}f(),a.parensInOp=!0,b.parensInOp=!0,g=new x.Operation(c,[g||a,b],i),i=h(v,y-1)}return g||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(e);return b?(g.push(y+h),h+=b[0].length,e=e.slice(b[1].length),f.push(b[1])):void 0}var c,d,e=C,f=[],g=[],h=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););if(f.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(h),""===f[0]&&(f.shift(),g.shift()),d=0;dl;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(a.toHSL().h)},saturation:function(a){return new d.Dimension(100*a.toHSL().s,"%")},lightness:function(a){return new d.Dimension(100*a.toHSL().l,"%")},hsvhue:function(a){return new d.Dimension(a.toHSV().h)},hsvsaturation:function(a){return new d.Dimension(100*a.toHSV().s,"%")},hsvvalue:function(a){return new d.Dimension(100*a.toHSV().v,"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(a.luma()*a.alpha*100,"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(b*a.alpha*100,"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("./fs"),i=c("path"),j=!1;if(arguments.length<2&&(g=f),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var k;try{k=c("mime")}catch(l){k=d._mime}f=k.lookup(g);var m=k.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(m)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var n=h.readFileSync(g),o=32,p=parseInt(n.length/1024,10);if(p>=o&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,p,o),new d.URL(e||b,this.currentFileInfo).eval(this.env);n=j?n.toString("base64"):encodeURIComponent(n);var q='"data:'+f+","+n+'"';return new d.URL(new d.Anonymous(q))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS()}).join(", ")+"]":a.toCSS()},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d,e){this.value=a,this.index=b,this.mapLines=d,this.currentFileInfo=c,this.rulesetLike="undefined"==typeof e?!1:e},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines,this.rulesetLike)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},isRulesetLike:function(){return this.rulesetLike},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},isRulesetLike:function(){return"@charset"!==this.name},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a":" > ","|":"|","^":" ^ ","^^":" ^^ "},_outputMapCompressed:{"":""," ":" ",":":" :","+":"+","~":"~",">":">","|":"|","^":"^","^^":"^^"},genCSS:function(a,b){b.add((a.compress?this._outputMapCompressed:this._outputMap)[this.value])},toCSS:a.toCSS}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={type:"Expression",accept:function(a){this.value&&(this.value=a.visitArray(this.value))},eval:function(b){var c,d=this.parens&&!this.parensInOp,e=!1;return d&&b.inParenthesis(),this.value.length>1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0,!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)t.value(h),s[h]=d.matchCondition(e,b);(s[0]||s[1])&&(s[0]!=s[1]&&(l.group=s[1]?v:w),r.push(l))}else r.push(l);q=!0}}for(t.reset(),n=[0,0,0],g=0;g0)m=w;else if(m=v,n[v]+n[w]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gh;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;ii;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames?this.frames.concat(c.frames):c.frames),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b,c;return"Quoted"!==a.type||this.escaped||a.escaped?(b=this.toCSS(),c=a.toCSS()):(b=a.value,c=this.value),b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function b(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(b,c,d,e,f,g,h){this.name=b,this.value=c instanceof a.Value||c instanceof a.Ruleset?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.merge=e,this.index=f,this.currentFileInfo=g,this.inline=h||!1,this.variable=b.charAt&&"@"===b.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(c){var d,e=!1,f=this.name;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:b(c,f)),"font"!==f||c.strictMath||(e=!0,c.strictMath=!0);try{if(d=this.value.eval(c),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline)}catch(g){throw"number"!=typeof g.index&&(g.index=this.index,g.filename=this.currentFileInfo.filename),g}finally{e&&(c.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;fb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){function d(b,c){return b.rules?!0:b instanceof a.Media||c&&b instanceof a.Comment?!0:b instanceof a.Directive||b instanceof a.Anonymous?b.isRulesetLike():!1}var e,f,g,h,i,j,k=[],l=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var m,n=b.compress?"":Array(b.tabLevel+1).join(" "),o=b.compress?"":Array(b.tabLevel).join(" ");for(e=0;ee;e++)if(j=q[e],p=j.length)for(e>0&&c.add(m),b.firstSelector=!0,j[0].genCSS(b,c),b.firstSelector=!1,f=1;p>f;f++)j[f].genCSS(b,c);c.add((b.compress?"{":" {\n")+n)}for(e=0;ee;e++)m&&c.add(m),l[e].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename);var j=g||i in e.recursionDetector;h.importMultiple||(b.skip=j?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var k=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};return!d||(b.root=d,b.importedFilename=i,f||!h.importMultiple&&j)?void k():(e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,k,h,e.onceFileDetectionMap,e.recursionDetector).run(d))})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.ruleset),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b.rules&&b.rules.rules&&this._mergeRules(b.rules.rules),b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var e,f=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?e=this._sourceMapURL:this._sourceMapFilename&&(e=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(f):e="data:application/json;base64,"+c("./encoder.js").encodeBase64(f),e&&this._css.push("/*# sourceMappingURL="+e+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0 && result[result.length-1] !== '..') { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - op = $char('/') || $char('*'); - - if (!op) { break; } - - a = this.operand(); - - if (!a) { break; } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().h)); - }, - saturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(Math.round(color.toHSV().h)); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/less-rhino-1.7.3.js b/dist/less-rhino-1.7.3.js deleted file mode 100644 index 75d08ff57..000000000 --- a/dist/less-rhino-1.7.3.js +++ /dev/null @@ -1,9317 +0,0 @@ -/* Less.js v1.7.3 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0 && result[result.length-1] !== '..') { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(tree.Anonymous)(match[1]); - } - }, - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environemnt, 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. - // - "import": function () { - var path, features, index = i; - - save(); - - var dir = $re(/^@import?\s+/); - - var options = (dir ? this.importOptions() : null) || {}; - - if (dir && (path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - if ($char(';')) { - forget(); - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - } - - restore(); - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - - save(); - - op = $char('/') || $char('*'); - - if (!op) { forget(); break; } - - a = this.operand(); - - if (!a) { restore(); break; } - forget(); - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - - if (!env.importMultiple) { - if (importedAtRoot) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/less-rhino-1.7.4.js b/dist/less-rhino-1.7.4.js deleted file mode 100644 index 1f2f17d07..000000000 --- a/dist/less-rhino-1.7.4.js +++ /dev/null @@ -1,9354 +0,0 @@ -/* Less.js v1.7.4 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0 && result[result.length-1] !== '..') { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg, index) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - if (!elements) - error("Missing target selector for :extend()."); - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(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. - // - "import": function () { - var path, features, index = i; - - var dir = $re(/^@import?\s+/); - - if (dir) { - var options = (dir ? this.importOptions() : null) || {}; - - if ((path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - - if (!$(';')) { - i = index; - error("missing semi-colon or unrecognised media features on import"); - } - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - else - { - i = index; - error("malformed import statement"); - } - } - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - - save(); - - op = $char('/') || $char('*'); - - if (!op) { forget(); break; } - - a = this.operand(); - - if (!a) { restore(); break; } - forget(); - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; - this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - isRulesetLike: function() { - return this.rulesetLike; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - isRulesetLike: function() { - return "@charset" !== this.name; - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = name.charAt && (name.charAt(0) === '@'); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - function isRulesetLikeNode(rule, root) { - // if it has nested rules, then it should be treated like a ruleset - if (rule.rules) - return true; - - // medias and comments do not have nested rules, but should be treated like rulesets anyway - if ( (rule instanceof tree.Media) || (root && rule instanceof tree.Comment)) - return true; - - // some directives and anonumoust nodes are ruleset like, others are not - if ((rule instanceof tree.Directive) || (rule instanceof tree.Anonymous)) { - return rule.isRulesetLike(); - } - - //anything else is assumed to be a rule - return false; - } - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (isRulesetLikeNode(rule, this.root)) { - rulesetNodes.push(rule); - } else { - ruleNodes.push(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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { - e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; - } - - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - if (!env.importMultiple) { - if (duplicateImport) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.ruleset); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); - -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - if (directiveNode.rules && directiveNode.rules.rules) { - this._mergeRules(directiveNode.rules.rules); - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/lessc-rhino-1.7.2.js b/dist/lessc-rhino-1.7.2.js deleted file mode 100644 index 40e44ad7d..000000000 --- a/dist/lessc-rhino-1.7.2.js +++ /dev/null @@ -1,449 +0,0 @@ -/* Less.js v1.7.2 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - var message = formatError(ctx, options); - throw new Error(message); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } -}(arguments)); diff --git a/dist/lessc-rhino-1.7.3.js b/dist/lessc-rhino-1.7.3.js deleted file mode 100644 index c27260ba0..000000000 --- a/dist/lessc-rhino-1.7.3.js +++ /dev/null @@ -1,449 +0,0 @@ -/* Less.js v1.7.3 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - var message = formatError(ctx, options); - throw new Error(message); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } -}(arguments)); diff --git a/dist/lessc-rhino-1.7.4.js b/dist/lessc-rhino-1.7.4.js deleted file mode 100644 index 4ae5b1da2..000000000 --- a/dist/lessc-rhino-1.7.4.js +++ /dev/null @@ -1,449 +0,0 @@ -/* Less.js v1.7.4 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - var message = formatError(ctx, options); - throw new Error(message); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } -}(arguments)); From 16746e9b1eca8e5cbf0b2fb9f8e412a5ad26e95a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 17:55:46 +0100 Subject: [PATCH 049/161] untangle the mess of dependencies and remove all circular dependencies. Remove un-necessary dependency injection. --- lib/less/browser.js | 4 +- lib/less/{tree => data}/unit-conversions.js | 0 lib/less/env.js | 257 +++--- lib/less/functions/default.js | 44 +- lib/less/functions/index.js | 6 +- lib/less/functions/types.js | 5 +- lib/less/non-node-index.js | 4 +- lib/less/parser.js | 9 +- lib/less/tree.js | 178 +--- lib/less/tree/alpha.js | 43 +- lib/less/tree/anonymous.js | 47 +- lib/less/tree/assignment.js | 43 +- lib/less/tree/attribute.js | 38 +- lib/less/tree/call.js | 99 +- lib/less/tree/color.js | 283 +++--- lib/less/tree/combinator.js | 25 +- lib/less/tree/comment.js | 38 +- lib/less/tree/condition.js | 74 +- lib/less/tree/debug-info.js | 34 + lib/less/tree/detached-ruleset.js | 28 +- lib/less/tree/dimension.js | 267 +++--- lib/less/tree/directive.js | 143 +-- lib/less/tree/element.js | 57 +- lib/less/tree/expression.js | 93 +- lib/less/tree/extend.js | 54 +- lib/less/tree/import.js | 147 +-- lib/less/tree/javascript.js | 66 +- lib/less/tree/js-eval-node.js | 53 ++ lib/less/tree/keyword.js | 36 +- lib/less/tree/media.js | 270 +++--- lib/less/tree/mixin-call.js | 235 ++--- lib/less/tree/mixin-definition.js | 254 +++-- lib/less/tree/negative.js | 32 +- lib/less/tree/node.js | 35 + lib/less/tree/operation.js | 80 +- lib/less/tree/paren.js | 26 +- lib/less/tree/quoted.js | 84 +- lib/less/tree/rule.js | 137 ++- lib/less/tree/ruleset-call.js | 19 +- lib/less/tree/ruleset.js | 971 ++++++++++---------- lib/less/tree/selector.js | 189 ++-- lib/less/tree/unicode-descriptor.js | 16 +- lib/less/tree/unit.js | 209 ++--- lib/less/tree/url.js | 73 +- lib/less/tree/value.js | 53 +- lib/less/tree/variable.js | 65 +- lib/less/visitor/import-visitor.js | 5 +- 47 files changed, 2434 insertions(+), 2494 deletions(-) rename lib/less/{tree => data}/unit-conversions.js (100%) create mode 100644 lib/less/tree/debug-info.js create mode 100644 lib/less/tree/js-eval-node.js create mode 100644 lib/less/tree/node.js diff --git a/lib/less/browser.js b/lib/less/browser.js index aed3d9994..61d769152 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -309,7 +309,7 @@ function loadStyles(modifyVars) { for (var i = 0; i < styles.length; i++) { style = styles[i]; if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(options), + var env = new less.contexts.parseEnv(options), lessText = style.innerHTML || ''; env.filename = document.location.href.replace(/#.*$/, ''); @@ -340,7 +340,7 @@ function loadStyles(modifyVars) { function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - var env = new less.tree.parseEnv(options); + var env = new less.contexts.parseEnv(options); env.mime = sheet.type; if (modifyVars || options.globalVars) { diff --git a/lib/less/tree/unit-conversions.js b/lib/less/data/unit-conversions.js similarity index 100% rename from lib/less/tree/unit-conversions.js rename to lib/less/data/unit-conversions.js diff --git a/lib/less/env.js b/lib/less/env.js index aff2888f2..5df2cf803 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -1,138 +1,139 @@ -module.exports = function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; +var contexts = {}; +module.exports = contexts; - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; +var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) { + if (!original) { return; } + + for(var i = 0; i < propertiesToCopy.length; i++) { + if (original.hasOwnProperty(propertiesToCopy[i])) { + destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } + } +}; + +var parseCopyProperties = [ + 'paths', // option - unmodified - paths to search for imports on + 'files', // list of files that have been imported, used for import-once + 'contents', // map - filename to contents of all the files + 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore + 'relativeUrls', // option - whether to adjust URL's to be relative + 'rootpath', // option - rootpath to append to URL's + 'strictImports', // option - + 'insecure', // option - whether to allow imports from insecure ssl hosts + 'dumpLineNumbers', // option - whether to dump line numbers + 'compress', // option - whether to compress + 'processImports', // option - whether to process imports. if false then imports will not be imported + 'syncImport', // option - whether to import synchronously + 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true + 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. + 'mime', // browser only - mime type for sheet import + 'useFileCache', // browser only - whether to use the per file session cache + 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. +]; + +//currentFileInfo = { +// 'relativeUrls' - option - whether to adjust URL's to be relative +// 'filename' - full resolved filename of current file +// 'rootpath' - path to append to normal URLs for this node +// 'currentDirectory' - path to the current file, absolute +// 'rootFilename' - filename of the base file +// 'entryPath' - absolute path to the entry file +// 'reference' - whether the file should not be output and only output parts that are referenced + +contexts.parseEnv = function(options) { + copyFromOriginal(options, this, parseCopyProperties); + + if (!this.contents) { this.contents = {}; } + if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } + if (!this.files) { this.files = {}; } + + if (typeof this.paths === "string") { this.paths = [this.paths]; } + + if (!this.currentFileInfo) { + var filename = (options && options.filename) || "input"; + var entryPath = filename.replace(/[^\/\\]*$/, ""); + if (options) { + options.filename = null; } + this.currentFileInfo = { + filename: filename, + relativeUrls: this.relativeUrls, + rootpath: (options && options.rootpath) || "", + currentDirectory: entryPath, + entryPath: entryPath, + rootFilename: filename + }; + } +}; - return path.join("/"); - }; +var evalCopyProperties = [ + 'silent', // whether to swallow errors and warnings + 'verbose', // whether to log more activity + 'compress', // whether to compress + 'yuicompress', // whether to compress with the outside tool yui compressor + 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) + 'strictMath', // whether math has to be within parenthesis + 'strictUnits', // whether units need to evaluate correctly + 'cleancss', // whether to compress with clean-css + 'sourceMap', // whether to output a source map + 'importMultiple', // whether we are currently importing multiple copies + 'urlArgs' // whether to add args into url tokens + ]; - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; +contexts.evalEnv = function(options, frames) { + copyFromOriginal(options, this, evalCopyProperties); - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } + this.frames = frames || []; +}; + +contexts.evalEnv.prototype.inParenthesis = function () { + if (!this.parensStack) { + this.parensStack = []; + } + this.parensStack.push(true); +}; - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } +contexts.evalEnv.prototype.outOfParenthesis = function () { + this.parensStack.pop(); +}; + +contexts.evalEnv.prototype.isMathOn = function () { + return this.strictMath ? (this.parensStack && this.parensStack.length) : true; +}; + +contexts.evalEnv.prototype.isPathRelative = function (path) { + return !/^(?:[a-z-]+:|\/)/.test(path); +}; + +contexts.evalEnv.prototype.normalizePath = function( path ) { + var + segments = path.split("/").reverse(), + segment; + + path = []; + while (segments.length !== 0 ) { + segment = segments.pop(); + switch( segment ) { + case ".": + break; + case "..": + if ((path.length === 0) || (path[path.length - 1] === "..")) { + path.push( segment ); + } else { + path.pop(); + } + break; + default: + path.push( segment ); + break; } - }; + } + return path.join("/"); }; + +//todo - do the same for the toCSS env +//tree.toCSSEnv = function (options) { +//}; + + diff --git a/lib/less/functions/default.js b/lib/less/functions/default.js index 6aa78aaa4..ee256c719 100644 --- a/lib/less/functions/default.js +++ b/lib/less/functions/default.js @@ -1,25 +1,23 @@ -module.exports = function(functions, tree) { - var defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } - }; +var Keyword = require("../tree/keyword.js"); - functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); - tree.defaultFunc = defaultFunc; +var defaultFunc = { + eval: function () { + var v = this.value_, e = this.error_; + if (e) { + throw e; + } + if (v != null) { + return v ? Keyword.True : Keyword.False; + } + }, + value: function (v) { + this.value_ = v; + }, + error: function (e) { + this.error_ = e; + }, + reset: function () { + this.value_ = this.error_ = null; + } }; +module.exports = defaultFunc; diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js index 36d716760..4600500ae 100644 --- a/lib/less/functions/index.js +++ b/lib/less/functions/index.js @@ -7,7 +7,11 @@ module.exports = function(less, tree) { require("./color.js")(functions, tree); require("./color-blending.js")(functions, tree); require("./data-uri.js")(functions, tree, less); - require("./default.js")(functions, tree); + + var defaultFunc = require("./default.js"); + functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); + tree.defaultFunc = defaultFunc; + require("./math.js")(functions, tree); require("./number.js")(functions, tree); require("./string.js")(functions, tree); diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js index 802d8094b..047343881 100644 --- a/lib/less/functions/types.js +++ b/lib/less/functions/types.js @@ -1,9 +1,10 @@ +var Keyword = require("../tree/keyword.js"); module.exports = function(functions, tree) { var isa = function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; + return (n instanceof Type) ? Keyword.True : Keyword.False; }, isunit = function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; + return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False; }; functions.functionRegistry.addMultiple({ iscolor: function (n) { diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index c93846df4..7a7e0f5fb 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -5,11 +5,11 @@ var less = { } }; -less.tree = (require('./tree'))(less, less.data); +less.tree = require('./tree'); less.visitor = require('./visitor/index.js')(less, less.tree); less.Parser = (require('./parser'))(less, less.tree, less.visitor); less.functions = (require('./functions/index.js'))(less, less.tree); -require('./env')(less.tree); +less.contexts = require("./env.js"); less.tree.sourceMapOutput = require('./source-map-output.js')(less); diff --git a/lib/less/parser.js b/lib/less/parser.js index 05e0d6c36..61fcd9359 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,5 +1,6 @@ var chunker = require('./chunker.js'); var LessError = require('./less-error.js'); +var contexts = require("./env.js"); module.exports = function(less, tree, visitor) { // @@ -51,8 +52,8 @@ var Parser = function Parser(env) { // Top parser on an import tree must be sure there is one "env" // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); + if (!(env instanceof contexts.parseEnv)) { + env = new contexts.parseEnv(env); } var imports = this.imports = { @@ -109,7 +110,7 @@ var Parser = function Parser(env) { } newFileInfo.filename = resolvedFilename; - var newEnv = new tree.parseEnv(env); + var newEnv = new contexts.parseEnv(env); newEnv.currentFileInfo = newFileInfo; newEnv.processImports = false; @@ -451,7 +452,7 @@ var Parser = function Parser(env) { options = options || {}; var evaldRoot, css, - evalEnv = new tree.evalEnv(options); + evalEnv = new contexts.evalEnv(options); // // Allows setting variables with a hash, so: diff --git a/lib/less/tree.js b/lib/less/tree.js index b3d7b1c59..ed72dc472 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -1,144 +1,40 @@ -module.exports = function (less, data) { - var tree = {}; -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -tree.Alpha = require('./tree/alpha')(tree); -tree.Color = require('./tree/color')(data, tree); -tree.Directive = require('./tree/directive')(tree); -tree.DetachedRuleset = require('./tree/detached-ruleset')(tree); -tree.Operation = require('./tree/operation')(tree); -tree.Dimension = require('./tree/dimension')(tree, require('./tree/unit-conversions')); //todo move conversions -tree.Unit = require('./tree/unit')(tree, require('./tree/unit-conversions')); -tree.Keyword = require('./tree/keyword')(tree); -tree.Variable = require('./tree/variable')(tree); -tree.Ruleset = require('./tree/ruleset')(tree); -tree.Element = require('./tree/element')(tree); -tree.Attribute = require('./tree/attribute')(tree); -tree.Combinator = require('./tree/combinator')(tree); -tree.Selector = require('./tree/selector')(tree); -tree.Quoted = require('./tree/quoted')(tree); -tree.Expression = require('./tree/expression')(tree); -tree.Rule = require('./tree/rule')(tree); -tree.Call = require('./tree/call')(tree, less); -tree.URL = require('./tree/url')(tree); -tree.Import = require('./tree/import')(tree); +tree.Alpha = require('./tree/alpha'); +tree.Color = require('./tree/color'); +tree.Directive = require('./tree/directive'); +tree.DetachedRuleset = require('./tree/detached-ruleset'); +tree.Operation = require('./tree/operation'); +tree.Dimension = require('./tree/dimension'); +tree.Unit = require('./tree/unit'); +tree.Keyword = require('./tree/keyword'); +tree.Variable = require('./tree/variable'); +tree.Ruleset = require('./tree/ruleset'); +tree.Element = require('./tree/element'); +tree.Attribute = require('./tree/attribute'); +tree.Combinator = require('./tree/combinator'); +tree.Selector = require('./tree/selector'); +tree.Quoted = require('./tree/quoted'); +tree.Expression = require('./tree/expression'); +tree.Rule = require('./tree/rule'); +tree.Call = require('./tree/call'); +tree.URL = require('./tree/url'); +tree.Import = require('./tree/import'); tree.mixin = { - Call: require('./tree/mixin-call')(tree), - Definition: require('./tree/mixin-definition')(tree) -}; -tree.Comment = require('./tree/comment')(tree); -tree.Anonymous = require('./tree/anonymous')(tree); -tree.Value = require('./tree/value')(tree); -tree.JavaScript = require('./tree/javascript')(tree); -tree.Assignment = require('./tree/assignment')(tree); -tree.Condition = require('./tree/condition')(tree); -tree.Paren = require('./tree/paren')(tree); -tree.Media = require('./tree/media')(tree); -tree.UnicodeDescriptor = require('./tree/unicode-descriptor')(tree); -tree.Negative = require('./tree/negative')(tree); -tree.Extend = require('./tree/extend')(tree); -tree.RulesetCall = require('./tree/ruleset-call')(tree); - -tree.fround = function(env, value) { - var precision = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (precision == null) ? value : Number((value + 2e-16).toFixed(precision)); -}; - -return tree; - -}; + Call: require('./tree/mixin-call'), + Definition: require('./tree/mixin-definition') +}; +tree.Comment = require('./tree/comment'); +tree.Anonymous = require('./tree/anonymous'); +tree.Value = require('./tree/value'); +tree.JavaScript = require('./tree/javascript'); +tree.Assignment = require('./tree/assignment'); +tree.Condition = require('./tree/condition'); +tree.Paren = require('./tree/paren'); +tree.Media = require('./tree/media'); +tree.UnicodeDescriptor = require('./tree/unicode-descriptor'); +tree.Negative = require('./tree/negative'); +tree.Extend = require('./tree/extend'); +tree.RulesetCall = require('./tree/ruleset-call'); + +module.exports = tree; diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js index eb247adf5..c4a7dd118 100644 --- a/lib/less/tree/alpha.js +++ b/lib/less/tree/alpha.js @@ -1,29 +1,28 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Alpha = function (val) { this.value = val; }; -Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); +Alpha.prototype = new Node(); +Alpha.prototype.type = "Alpha"; - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS +Alpha.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); }; -return Alpha; +Alpha.prototype.eval = function (env) { + if (this.value.eval) { return new Alpha(this.value.eval(env)); } + return this; }; +Alpha.prototype.genCSS = function (env, output) { + output.add("alpha(opacity="); + + if (this.value.genCSS) { + this.value.genCSS(env, output); + } else { + output.add(this.value); + } + + output.add(")"); +}; + +module.exports = Alpha; diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 711579f41..4c1695e1f 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { this.value = value; @@ -7,32 +7,29 @@ var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) this.currentFileInfo = currentFileInfo; this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike; }; -Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } +Anonymous.prototype = new Node(); +Anonymous.prototype.type = "Anonymous"; +Anonymous.prototype.eval = function () { + return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); +}; +Anonymous.prototype.compare = function (x) { + if (!x.toCSS) { + return -1; + } - var left = this.toCSS(), - right = x.toCSS(); + var left = this.toCSS(), + right = x.toCSS(); - if (left === right) { - return 0; - } + if (left === right) { + return 0; + } - return left < right ? -1 : 1; - }, - isRulesetLike: function() { - return this.rulesetLike; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS + return left < right ? -1 : 1; +}; +Anonymous.prototype.isRulesetLike = function() { + return this.rulesetLike; }; -return Anonymous; +Anonymous.prototype.genCSS = function (env, output) { + output.add(this.value, this.currentFileInfo, this.index, this.mapLines); }; +module.exports = Anonymous; diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index afe62933b..efcc9030d 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -1,29 +1,28 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Assignment = function (key, val) { this.key = key; this.value = val; }; -Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS + +Assignment.prototype = new Node(); +Assignment.prototype.type = "Assignment"; +Assignment.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Assignment.prototype.eval = function (env) { + if (this.value.eval) { + return new(Assignment)(this.key, this.value.eval(env)); + } + return this; }; -return Assignment; +Assignment.prototype.genCSS = function (env, output) { + output.add(this.key + '='); + if (this.value.genCSS) { + this.value.genCSS(env, output); + } else { + output.add(this.value); + } }; +module.exports = Assignment; + diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js index 4103bfc4a..4d713f2bc 100644 --- a/lib/less/tree/attribute.js +++ b/lib/less/tree/attribute.js @@ -1,29 +1,27 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Attribute = function (key, op, value) { this.key = key; this.op = op; this.value = value; }; -Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } +Attribute.prototype = new Node(); +Attribute.prototype.type = "Attribute"; +Attribute.prototype.eval = function (env) { + return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key, + this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); +}; +Attribute.prototype.genCSS = function (env, output) { + output.add(this.toCSS(env)); +}; +Attribute.prototype.toCSS = function (env) { + var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - return '[' + value + ']'; + if (this.op) { + value += this.op; + value += (this.value.toCSS ? this.value.toCSS(env) : this.value); } + + return '[' + value + ']'; }; -return Attribute; -}; +module.exports = Attribute; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 08dc98986..f7cbfed6d 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -1,5 +1,4 @@ -module.exports = function (tree, less) { - +var Node = require("./node.js"); // // A function call node. // @@ -9,61 +8,57 @@ var Call = function (name, args, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; }; -Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - this.args = visitor.visitArray(this.args); - } - }, - // - // When evaluating a function call, - // we either find the function in `less.functions` [1], - // in which case we call it, passing the evaluated arguments, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - result, funcCaller = new less.functions.functionCaller(this.name, env, this.currentFileInfo); +Call.prototype = new Node(); +Call.prototype.type = "Call"; +Call.prototype.accept = function (visitor) { + if (this.args) { + this.args = visitor.visitArray(this.args); + } +}; +// +// When evaluating a function call, +// we either find the function in `less.functions` [1], +// in which case we call it, passing the evaluated arguments, +// if this returns null or we cannot find the function, 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. +// +Call.prototype.eval = function (env) { + var args = this.args.map(function (a) { return a.eval(env); }), + FunctionCaller = require("../non-node-index.js").functions.functionCaller, //TODO! Move out + result, funcCaller = new FunctionCaller(this.name, env, this.currentFileInfo); - if (funcCaller.isValid()) { // 1. - try { - result = funcCaller.call(args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; + if (funcCaller.isValid()) { // 1. + try { + result = funcCaller.call(args); + if (result != null) { + return result; } + } catch (e) { + throw { type: e.type || "Runtime", + message: "error evaluating function `" + this.name + "`" + + (e.message ? ': ' + e.message : ''), + index: this.index, filename: this.currentFileInfo.filename }; } + } - return new Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); + return new Call(this.name, args, this.index, this.currentFileInfo); +}; +Call.prototype.genCSS = function (env, output) { + output.add(this.name + "(", this.currentFileInfo, this.index); - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } + for(var i = 0; i < this.args.length; i++) { + this.args[i].genCSS(env, output); + if (i + 1 < this.args.length) { + output.add(", "); } + } - output.add(")"); - }, - - toCSS: tree.toCSS -}; -return Call; + output.add(")"); }; +module.exports = Call; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 533235f85..5a51d1c64 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -1,4 +1,6 @@ -module.exports = function (data, tree) { +var Node = require("./node.js"), + colors = require("../data/colors.js"); + // // RGB Colors - #ff0014, #eee // @@ -23,150 +25,157 @@ var Color = function (rgb, a) { this.alpha = typeof(a) === 'number' ? a : 1; }; -Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, color, alpha; - - // `keyword` is set if this color was originally - // converted from a named color string so we need - // to respect this and try to output named color too. - if (this.keyword) { - return this.keyword; - } +Color.prototype = new Node(); +Color.prototype.type = "Color"; - // 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. - alpha = tree.fround(env, this.alpha); - if (alpha < 1) { - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } +function clamp(v, max) { + return Math.min(Math.max(v, 0), max); +} - color = this.toRGB(); +function toHex(v) { + return '#' + v.map(function (c) { + c = clamp(Math.round(c), 255); + return (c < 16 ? '0' : '') + c.toString(16); + }).join(''); +} - if (compress) { - var splitcolor = color.split(''); +Color.prototype.luma = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255; - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; - } - } + r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); + g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); + b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +}; +Color.prototype.genCSS = function (env, output) { + output.add(this.toCSS(env)); +}; +Color.prototype.toCSS = function (env, doNotCompress) { + var compress = env && env.compress && !doNotCompress, color, alpha; + + // `keyword` is set if this color was originally + // converted from a named color string so we need + // to respect this and try to output named color too. + if (this.keyword) { + return this.keyword; + } - return color; - }, + // 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. + alpha = this.fround(env, this.alpha); + if (alpha < 1) { + return "rgba(" + this.rgb.map(function (c) { + return clamp(Math.round(c), 255); + }).concat(clamp(alpha, 1)) + .join(',' + (compress ? '' : ' ')) + ")"; + } - // - // 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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } + color = this.toRGB(); - if (max === min) { - h = 0; - } else { - 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; + if (compress) { + var splitcolor = color.split(''); + + // Convert color to short format + if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { + color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5]; } - return { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!x.rgb) { - return -1; + } + + 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. +// +Color.prototype.operate = function (env, op, other) { + var rgb = []; + var alpha = this.alpha * (1 - other.alpha) + other.alpha; + for (var c = 0; c < 3; c++) { + rgb[c] = this._operate(env, op, this.rgb[c], other.rgb[c]); + } + return new(Color)(rgb, alpha); +}; +Color.prototype.toRGB = function () { + return toHex(this.rgb); +}; +Color.prototype.toHSL = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2, d = max - min; + + if (max === min) { + h = s = 0; + } else { + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; } + h /= 6; + } + return { 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 +Color.prototype.toHSV = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + + var d = max - min; + if (max === 0) { + s = 0; + } else { + s = d / max; + } - 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; + if (max === min) { + h = 0; + } else { + 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 { h: h * 360, s: s, v: v, a: a }; +}; +Color.prototype.toARGB = function () { + return toHex([this.alpha * 255].concat(this.rgb)); +}; +Color.prototype.compare = function (x) { + if (!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; }; Color.fromKeyword = function(keyword) { var c, key = keyword.toLowerCase(); - if (data.colors.hasOwnProperty(key)) { - c = new(Color)(data.colors[key].slice(1)); + if (colors.hasOwnProperty(key)) { + c = new(Color)(colors[key].slice(1)); } else if (key === "transparent") { c = new(Color)([0, 0, 0], 0); @@ -177,16 +186,4 @@ Color.fromKeyword = function(keyword) { return c; } }; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} -return Color; -}; +module.exports = Color; diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js index 0e34baf9a..ba0abb093 100644 --- a/lib/less/tree/combinator.js +++ b/lib/less/tree/combinator.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Combinator = function (value) { if (value === ' ') { @@ -7,18 +7,15 @@ var Combinator = function (value) { this.value = value ? value.trim() : ""; } }; -Combinator.prototype = { - type: "Combinator", - _noSpaceCombinators: { - '': true, - ' ': true, - '|': true - }, - genCSS: function (env, output) { - var spaceOrEmpty = (env.compress || this._noSpaceCombinators[this.value]) ? '' : ' '; - output.add(spaceOrEmpty + this.value + spaceOrEmpty); - }, - toCSS: tree.toCSS +Combinator.prototype = new Node(); +Combinator.prototype.type = "Combinator"; +var _noSpaceCombinators = { + '': true, + ' ': true, + '|': true }; -return Combinator; +Combinator.prototype.genCSS = function (env, output) { + var spaceOrEmpty = (env.compress || _noSpaceCombinators[this.value]) ? '' : ' '; + output.add(spaceOrEmpty + this.value + spaceOrEmpty); }; +module.exports = Combinator; diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index 10c1e794d..42262319f 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -1,28 +1,28 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + getDebugInfo = require("./debug-info.js"); var Comment = function (value, isLineComment, index, currentFileInfo) { this.value = value; this.isLineComment = isLineComment; this.currentFileInfo = currentFileInfo; }; -Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value); - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && this.value[2] !== "!"; - return this.isLineComment || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; +Comment.prototype = new Node(); +Comment.prototype.type = "Comment"; +Comment.prototype.genCSS = function (env, output) { + if (this.debugInfo) { + output.add(getDebugInfo(env, this), this.currentFileInfo, this.index); } + output.add(this.value); }; -return Comment; +Comment.prototype.isSilent = function(env) { + var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), + isCompressed = env.compress && this.value[2] !== "!"; + return this.isLineComment || isReference || isCompressed; }; +Comment.prototype.markReferenced = function () { + this.isReferenced = true; +}; +Comment.prototype.isRulesetLike = function(root) { + return Boolean(root); +}; +module.exports = Comment; diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index fdc798121..464c96c59 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Condition = function (op, l, r, i, negate) { this.op = op.trim(); @@ -7,43 +7,41 @@ var Condition = function (op, l, r, i, negate) { this.index = i; this.negate = negate; }; -Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); +Condition.prototype = new Node(); +Condition.prototype.type = "Condition"; +Condition.prototype.accept = function (visitor) { + this.lvalue = visitor.visit(this.lvalue); + this.rvalue = visitor.visit(this.rvalue); +}; +Condition.prototype.eval = function (env) { + var a = this.lvalue.eval(env), + b = this.rvalue.eval(env); - var i = this.index, result; + var i = this.index, result; - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; -return Condition; + result = (function (op) { + switch (op) { + case 'and': + return a && b; + case 'or': + return a || b; + default: + if (a.compare) { + result = a.compare(b); + } else if (b.compare) { + result = b.compare(a); + } else { + throw { type: "Type", + message: "Unable to perform comparison", + index: i }; + } + switch (result) { + case -1: return op === '<' || op === '=<' || op === '<='; + case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; + case 1: return op === '>' || op === '>='; + } + } + })(this.op); + return this.negate ? !result : result; }; +module.exports = Condition; diff --git a/lib/less/tree/debug-info.js b/lib/less/tree/debug-info.js new file mode 100644 index 000000000..5af873fc6 --- /dev/null +++ b/lib/less/tree/debug-info.js @@ -0,0 +1,34 @@ +var debugInfo = function(env, ctx, lineSeperator) { + var result=""; + if (env.dumpLineNumbers && !env.compress) { + switch(env.dumpLineNumbers) { + case 'comments': + result = debugInfo.asComment(ctx); + break; + case 'mediaquery': + result = debugInfo.asMediaQuery(ctx); + break; + case 'all': + result = debugInfo.asComment(ctx) + (lineSeperator || "") + debugInfo.asMediaQuery(ctx); + break; + } + } + return result; +}; + +debugInfo.asComment = function(ctx) { + return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; +}; + +debugInfo.asMediaQuery = function(ctx) { + return '@media -sass-debug-info{filename{font-family:' + + ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { + if (a == '\\') { + a = '\/'; + } + return '\\' + a; + }) + + '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; +}; + +module.exports = debugInfo; diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js index a58029cad..009d67c2e 100644 --- a/lib/less/tree/detached-ruleset.js +++ b/lib/less/tree/detached-ruleset.js @@ -1,21 +1,21 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + contexts = require("../env.js"); var DetachedRuleset = function (ruleset, frames) { this.ruleset = ruleset; this.frames = frames; }; -DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } +DetachedRuleset.prototype = new Node(); +DetachedRuleset.prototype.type = "DetachedRuleset"; +DetachedRuleset.prototype.evalFirst = true; +DetachedRuleset.prototype.accept = function (visitor) { + this.ruleset = visitor.visit(this.ruleset); }; -return DetachedRuleset; +DetachedRuleset.prototype.eval = function (env) { + var frames = this.frames || env.frames.slice(0); + return new DetachedRuleset(this.ruleset, frames); }; +DetachedRuleset.prototype.callEval = function (env) { + return this.ruleset.eval(this.frames ? new(contexts.evalEnv)(env, this.frames.concat(env.frames)) : env); +}; +module.exports = DetachedRuleset; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index 2b24a8cb3..84b92c0ff 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -1,167 +1,164 @@ -module.exports = function (tree, unitConversions) { +var Node = require("./node.js"), + unitConversions = require("../data/unit-conversions.js"), + Unit = require("./unit.js"), + Color = require("./color.js"); // // A number with a unit // var Dimension = function (value, unit) { this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); + this.unit = (unit && unit instanceof Unit) ? unit : + new(Unit)(unit ? [unit] : undefined); }; -Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } +Dimension.prototype = new Node(); +Dimension.prototype.type = "Dimension"; +Dimension.prototype.accept = function (visitor) { + this.unit = visitor.visit(this.unit); +}; +Dimension.prototype.eval = function (env) { + return this; +}; +Dimension.prototype.toColor = function () { + return new(Color)([this.value, this.value, this.value]); +}; +Dimension.prototype.genCSS = function (env, output) { + if ((env && env.strictUnits) && !this.unit.isSingular()) { + throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); + } - var value = tree.fround(env, this.value), - strValue = String(value); + var value = this.fround(env, this.value), + strValue = String(value); - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } + if (value !== 0 && value < 0.000001 && value > -0.000001) { + // would be output 1e-6 etc. + strValue = value.toFixed(20).replace(/0+$/, ""); + } - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } + if (env && env.compress) { + // Zero values doesn't need a unit + if (value === 0 && this.unit.isLength()) { + output.add(strValue); + return; } - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); + // Float values doesn't need a leading zero + if (value > 0 && value < 1) { + strValue = (strValue).substr(1); } - return new(Dimension)(value, unit); - }, + } + + output.add(strValue); + this.unit.genCSS(env, output); +}; - compare: function (other) { - if (other instanceof Dimension) { - var a, b, - aValue, bValue; +// In an operation between two Dimensions, +// we default to the first Dimension's unit, +// so `1px + 2` will yield `3px`. +Dimension.prototype.operate = function (env, op, other) { + /*jshint noempty:false */ + var value = this._operate(env, op, this.value, other.value), + unit = this.unit.clone(); + + if (op === '+' || op === '-') { + if (unit.numerator.length === 0 && unit.denominator.length === 0) { + unit.numerator = other.unit.numerator.slice(0); + unit.denominator = other.unit.denominator.slice(0); + } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { + // do nothing + } else { + other = other.convertTo(this.unit.usedUnits()); - 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; - } + if(env.strictUnits && other.unit.toString() !== unit.toString()) { + throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + + "' and '" + other.unit.toString() + "'."); } - aValue = a.value; - bValue = b.value; - if (bValue > aValue) { + value = this._operate(env, op, this.value, other.value); + } + } else if (op === '*') { + unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); + unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); + unit.cancel(); + } else if (op === '/') { + unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); + unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); + unit.cancel(); + } + return new(Dimension)(value, unit); +}; +Dimension.prototype.compare = function (other) { + if (other instanceof Dimension) { + var a, b, + aValue, bValue; + + 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; } - } else { + } + aValue = a.value; + bValue = b.value; + + if (bValue > aValue) { return -1; + } else if (bValue < aValue) { + return 1; + } else { + return 0; } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in unitConversions) { - if (unitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } + } else { + return -1; + } +}; +Dimension.prototype.unify = function () { + return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); +}; +Dimension.prototype.convertTo = function (conversions) { + var value = this.value, unit = this.unit.clone(), + i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; + + if (typeof conversions === 'string') { + for(i in unitConversions) { + if (unitConversions[i].hasOwnProperty(conversions)) { + derivedConversions = {}; + derivedConversions[i] = conversions; } - conversions = derivedConversions; } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; + conversions = derivedConversions; + } + applyUnit = function (atomicUnit, denominator) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit)) { + if (denominator) { + value = value / (group[atomicUnit] / group[targetUnit]); + } else { + value = value * (group[atomicUnit] / group[targetUnit]); } - return atomicUnit; - }; + return targetUnit; + } - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = unitConversions[groupName]; + return atomicUnit; + }; - unit.map(applyUnit); - } + for (groupName in conversions) { + if (conversions.hasOwnProperty(groupName)) { + targetUnit = conversions[groupName]; + group = unitConversions[groupName]; + + unit.map(applyUnit); } + } - unit.cancel(); + unit.cancel(); - return new(Dimension)(value, unit); - } -}; -return Dimension; + return new(Dimension)(value, unit); }; +module.exports = Dimension; diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index 7789ba30a..2f99d3f18 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -1,4 +1,5 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Ruleset = require("./ruleset.js"); var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { this.name = name; @@ -12,64 +13,92 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) this.debugInfo = debugInfo; }; -Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - isRulesetLike: function() { - return !this.isCharset(); - }, - isCharset: function() { - return "@charset" === this.name; - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } +Directive.prototype = new Node(); +Directive.prototype.type = "Directive"; +Directive.prototype.accept = function (visitor) { + var value = this.value, rules = this.rules; + if (rules) { + rules = visitor.visit(rules); + } + if (value) { + value = visitor.visit(value); + } +}; +Directive.prototype.isRulesetLike = function() { + return this.rules || !this.isCharset(); +}; +Directive.prototype.isCharset = function() { + return "@charset" === this.name; +}; +Directive.prototype.genCSS = function (env, output) { + var value = this.value, rules = this.rules; + output.add(this.name, this.currentFileInfo, this.index); + if (value) { + output.add(' '); + value.genCSS(env, output); + } + if (rules) { + this.outputRuleset(env, output, [rules]); + } else { + output.add(';'); + } +}; +Directive.prototype.eval = function (env) { + var value = this.value, rules = this.rules; + if (value) { + value = value.eval(env); + } + if (rules) { + rules = rules.eval(env); + rules.root = true; + } + return new(Directive)(this.name, value, rules, + this.index, this.currentFileInfo, this.debugInfo); +}; +Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); }; +Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); }; +Directive.prototype.rulesets = function () { if (this.rules) return Ruleset.prototype.rulesets.apply(this.rules); }; +Directive.prototype.markReferenced = function () { + var i, rules; + this.isReferenced = true; + if (this.rules) { + rules = this.rules.rules; + for (i = 0; i < rules.length; i++) { + if (rules[i].markReferenced) { + rules[i].markReferenced(); } } } }; -return Directive; +Directive.prototype.outputRuleset = function (env, output, rules) { + var ruleCnt = rules.length, i; + env.tabLevel = (env.tabLevel | 0) + 1; + + // Compressed + if (env.compress) { + output.add('{'); + for (i = 0; i < ruleCnt; i++) { + rules[i].genCSS(env, output); + } + output.add('}'); + env.tabLevel--; + return; + } + + // Non-compressed + var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; + if (!ruleCnt) { + output.add(" {" + tabSetStr + '}'); + } else { + output.add(" {" + tabRuleStr); + rules[0].genCSS(env, output); + for (i = 1; i < ruleCnt; i++) { + output.add(tabRuleStr); + rules[i].genCSS(env, output); + } + output.add(tabSetStr + '}'); + } + + env.tabLevel--; }; +module.exports = Directive; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 925b4b936..8142d11f1 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -1,8 +1,9 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Combinator = require("./combinator.js"); var Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); + this.combinator = combinator instanceof Combinator ? + combinator : new(Combinator)(combinator); if (typeof(value) === 'string') { this.value = value.trim(); @@ -14,32 +15,30 @@ var Element = function (combinator, value, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; }; -Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } +Element.prototype = new Node(); +Element.prototype.type = "Element"; +Element.prototype.accept = function (visitor) { + var value = this.value; + this.combinator = visitor.visit(this.combinator); + if (typeof value === "object") { + this.value = visitor.visit(value); } }; -return Element; +Element.prototype.eval = function (env) { + return new(Element)(this.combinator, + this.value.eval ? this.value.eval(env) : this.value, + this.index, + this.currentFileInfo); }; +Element.prototype.genCSS = function (env, output) { + output.add(this.toCSS(env), this.currentFileInfo, this.index); +}; +Element.prototype.toCSS = function (env) { + var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); + if (value === '' && this.combinator.value.charAt(0) === '&') { + return ''; + } else { + return this.combinator.toCSS(env || {}) + value; + } +}; +module.exports = Element; diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 4614e1559..11d09cb22 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -1,54 +1,53 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Paren = require("./paren.js"), + Comment = require("./comment.js"); var Expression = function (value) { this.value = value; }; -Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); +Expression.prototype = new Node(); +Expression.prototype.type = "Expression"; +Expression.prototype.accept = function (visitor) { + if (this.value) { + this.value = visitor.visitArray(this.value); + } +}; +Expression.prototype.eval = function (env) { + var returnValue, + inParenthesis = this.parens && !this.parensInOp, + doubleParen = false; + if (inParenthesis) { + env.inParenthesis(); + } + if (this.value.length > 1) { + returnValue = new(Expression)(this.value.map(function (e) { + return e.eval(env); + })); + } else if (this.value.length === 1) { + if (this.value[0].parens && !this.value[0].parensInOp) { + doubleParen = true; } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } + returnValue = this.value[0].eval(env); + } else { + returnValue = this; + } + if (inParenthesis) { + env.outOfParenthesis(); + } + if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { + returnValue = new(Paren)(returnValue); + } + return returnValue; +}; +Expression.prototype.genCSS = function (env, output) { + for(var i = 0; i < this.value.length; i++) { + this.value[i].genCSS(env, output); + if (i + 1 < this.value.length) { + output.add(" "); } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); } }; -return Expression; +Expression.prototype.throwAwayComments = function () { + this.value = this.value.filter(function(v) { + return !(v instanceof Comment); + }); }; +module.exports = Expression; diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js index 746bdeb38..d4d0676be 100644 --- a/lib/less/tree/extend.js +++ b/lib/less/tree/extend.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Extend = function Extend(selector, option, index) { this.selector = selector; @@ -20,34 +20,32 @@ var Extend = function Extend(selector, option, index) { }; Extend.next_id = 0; -Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; +Extend.prototype = new Node(); +Extend.prototype.type = "Extend"; +Extend.prototype.accept = function (visitor) { + this.selector = visitor.visit(this.selector); +}; +Extend.prototype.eval = function (env) { + return new(Extend)(this.selector.eval(env), this.option, this.index); +}; +Extend.prototype.clone = function (env) { + return new(Extend)(this.selector, this.option, this.index); +}; +Extend.prototype.findSelfSelectors = function (selectors) { + var selfElements = [], + i, + selectorElements; - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); + for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { + selectorElements[0].combinator.value = ' '; } - - this.selfSelectors = [{ elements: selfElements }]; + selfElements = selfElements.concat(selectors[i].elements); } + + this.selfSelectors = [{ elements: selfElements }]; }; -return Extend; -}; +module.exports = Extend; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 2042e2e0e..c0d045931 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -1,4 +1,10 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Media = require("./media.js"), + URL = require("./url.js"), + Quoted = require("./quoted.js"), + Ruleset = require("./ruleset.js"), + Anonymous = require("./anonymous.js"); + // // CSS @import node // @@ -37,88 +43,85 @@ var Import = function (path, features, options, index, currentFileInfo) { // we end up with a flat structure, which can easily be imported in the parent // ruleset. // -Import.prototype = { - type: "Import", - accept: function (visitor) { +Import.prototype = new Node(); +Import.prototype.type = "Import"; +Import.prototype.accept = function (visitor) { + if (this.features) { + this.features = visitor.visit(this.features); + } + this.path = visitor.visit(this.path); + if (!this.options.inline && this.root) { + this.root = visitor.visit(this.root); + } +}; +Import.prototype.genCSS = function (env, output) { + if (this.css) { + output.add("@import ", this.currentFileInfo, this.index); + this.path.genCSS(env, output); if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; + output.add(" "); + this.features.genCSS(env, output); } - return null; - }, - evalForImport: function (env) { - return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; + output.add(';'); + } +}; +Import.prototype.getPath = function () { + if (this.path instanceof Quoted) { + var path = this.path.value; + return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; + } else if (this.path instanceof URL) { + return this.path.value.value; + } + return null; +}; +Import.prototype.evalForImport = function (env) { + return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); +}; +Import.prototype.evalPath = function (env) { + var path = this.path.eval(env); + var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } + if (!(path instanceof URL)) { + if (rootpath) { + var pathValue = path.value; + // Add the base path if the import is relative + if (pathValue && env.isPathRelative(pathValue)) { + path.value = rootpath +pathValue; } - path.value = env.normalizePath(path.value); } + path.value = env.normalizePath(path.value); + } - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); + return path; +}; +Import.prototype.eval = function (env) { + var ruleset, features = this.features && this.features.eval(env); + if (this.skip) { + if (typeof this.skip === "function") { + this.skip = this.skip(); + } if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } + return []; } + } - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); + if (this.options.inline) { + //todo needs to reference css file not import + var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); + return this.features ? new(Media)([contents], this.features.value) : [contents]; + } else if (this.css) { + var newImport = new(Import)(this.evalPath(env), features, this.options, this.index); + if (!newImport.css && this.error) { + throw this.error; + } + return newImport; + } else { + ruleset = new(Ruleset)(null, this.root.rules.slice(0)); - ruleset.evalImports(env); + ruleset.evalImports(env); - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } + return this.features ? new(Media)(ruleset.rules, this.features.value) : ruleset.rules; } }; -return Import; -}; +module.exports = Import; diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index fdca7bf8a..a8f5533c8 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -1,57 +1,27 @@ -module.exports = function (tree) { +var JsEvalNode = require("./js-eval-node.js"), + Dimension = require("./dimension.js"), + Quoted = require("./quoted.js"), + Anonymous = require("./anonymous.js"); var JavaScript = function (string, index, escaped) { this.escaped = escaped; this.expression = string; this.index = index; }; -JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; +JavaScript.prototype = new JsEvalNode(); +JavaScript.prototype.type = "JavaScript"; +JavaScript.prototype.eval = function(env) { + var result = this.evaluateJavaScript(this.expression, env); - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } + if (typeof(result) === 'number') { + return new(Dimension)(result); + } else if (typeof(result) === 'string') { + return new(Quoted)('"' + result + '"', result, this.escaped, this.index); + } else if (Array.isArray(result)) { + return new(Anonymous)(result.join(', ')); + } else { + return new(Anonymous)(result); } }; -return JavaScript; -}; + +module.exports = JavaScript; diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js new file mode 100644 index 000000000..0693873ca --- /dev/null +++ b/lib/less/tree/js-eval-node.js @@ -0,0 +1,53 @@ +var Node = require("./node.js"), + Variable = require("./variable.js"); + +var jsEvalNode = function() { +}; +jsEvalNode.prototype = new Node(); + +jsEvalNode.prototype.evaluateJavaScript = function (expression, env) { + var result, + that = this, + context = {}; + + expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { + return that.jsify(new(Variable)('@' + name, that.index).eval(env)); + }); + + try { + expression = new(Function)('return (' + expression + ')'); + } catch (e) { + throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , + index: this.index }; + } + + var variables = env.frames[0].variables(); + for (var k in variables) { + if (variables.hasOwnProperty(k)) { + /*jshint loopfunc:true */ + context[k.slice(1)] = { + value: variables[k].value, + toJS: function () { + return this.value.eval(env).toCSS(); + } + }; + } + } + + try { + result = expression.call(context); + } catch (e) { + throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , + index: this.index }; + } + return result; +}; +jsEvalNode.prototype.jsify = function (obj) { + if (Array.isArray(obj.value) && (obj.value.length > 1)) { + return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; + } else { + return obj.toCSS(); + } +}; + +module.exports = jsEvalNode; diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 46f564841..7f99a7122 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -1,27 +1,21 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Keyword = function (value) { this.value = value; }; -Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } +Keyword.prototype = new Node(); +Keyword.prototype.type = "Keyword"; +Keyword.prototype.genCSS = function (env, output) { + if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } + output.add(this.value); +}; +Keyword.prototype.compare = function (other) { + if (other instanceof Keyword) { + return other.value === this.value ? 0 : 1; + } else { + return -1; } }; -//TODO move? -tree.True = new(Keyword)('true'); -tree.False = new(Keyword)('false'); - -return Keyword; +Keyword.True = new(Keyword)('true'); +Keyword.False = new(Keyword)('false'); -}; +module.exports = Keyword; diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 494e2293a..5926b324a 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -1,4 +1,10 @@ -module.exports = function (tree) { +var Ruleset = require("./ruleset.js"), + Value = require("./value.js"), + Element = require("./element.js"), + Selector = require("./selector.js"), + Anonymous = require("./anonymous.js"), + Expression = require("./expression.js"), + Directive = require("./directive.js"); var Media = function (value, features, index, currentFileInfo) { this.index = index; @@ -6,152 +12,150 @@ var Media = function (value, features, index, currentFileInfo) { var selectors = this.emptySelectors(); - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; + this.features = new(Value)(features); + this.rules = [new(Ruleset)(selectors, value)]; this.rules[0].allowImports = true; }; -Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } +Media.prototype = new Directive(); +Media.prototype.type = "Media"; +Media.prototype.isRulesetLike = true; +Media.prototype.accept = function (visitor) { + if (this.features) { + this.features = visitor.visit(this.features); + } + if (this.rules) { + this.rules = visitor.visitArray(this.rules); + } +}; +Media.prototype.genCSS = function (env, output) { + output.add('@media ', this.currentFileInfo, this.index); + this.features.genCSS(env, output); + this.outputRuleset(env, output, this.rules); +}; +Media.prototype.eval = function (env) { + if (!env.mediaBlocks) { + env.mediaBlocks = []; + env.mediaPath = []; + } - var media = new(Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } + var media = new(Media)(null, [], this.index, this.currentFileInfo); + if(this.debugInfo) { + this.rules[0].debugInfo = this.debugInfo; + media.debugInfo = this.debugInfo; + } + var strictMathBypass = false; + if (!env.strictMath) { + strictMathBypass = true; + env.strictMath = true; + } + try { + media.features = this.features.eval(env); + } + finally { + if (strictMathBypass) { + env.strictMath = false; } + } - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, + env.mediaPath.push(media); + env.mediaBlocks.push(media); - evalTop: function (env) { - var result = this; + env.frames.unshift(this.rules[0]); + media.rules = [this.rules[0].eval(env)]; + env.frames.shift(); - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; + env.mediaPath.pop(); + + return env.mediaPath.length === 0 ? media.evalTop(env) : + media.evalNested(env); +}; +//TODO merge with directive +Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); }; +Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); }; +Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); }; +Media.prototype.emptySelectors = function() { + var el = new(Element)('', '&', this.index, this.currentFileInfo), + sels = [new(Selector)([el], null, null, this.index, this.currentFileInfo)]; + sels[0].mediaEmpty = true; + return sels; +}; +Media.prototype.markReferenced = function () { + var i, rules = this.rules[0].rules; + this.rules[0].markReferenced(); + this.isReferenced = true; + for (i = 0; i < rules.length; i++) { + if (rules[i].markReferenced) { + rules[i].markReferenced(); } + } +}; +Media.prototype.evalTop = function (env) { + var result = this; + + // Render all dependent Media blocks. + if (env.mediaBlocks.length > 1) { + var selectors = this.emptySelectors(); + result = new(Ruleset)(selectors, env.mediaBlocks); + result.multiMedia = true; + } - delete env.mediaBlocks; - delete env.mediaPath; + delete env.mediaBlocks; + delete env.mediaPath; - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); + return result; +}; +Media.prototype.evalNested = function (env) { + var i, value, + path = env.mediaPath.concat([this]); + + // Extract the media-query conditions separated with `,` (OR). + for (i = 0; i < path.length; i++) { + value = path[i].features instanceof Value ? + path[i].features.value : path[i].features; + path[i] = Array.isArray(value) ? value : [value]; + } - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 + this.features = new(Value)(this.permute(path).map(function (path) { + path = path.map(function (fragment) { + return fragment.toCSS ? fragment : new(Anonymous)(fragment); + }); + + for(i = path.length - 1; i > 0; i--) { + path.splice(i, 0, new(Anonymous)("and")); } - // 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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } + return new(Expression)(path); + })); + + // Fake a tree-node that doesn't output anything. + return new(Ruleset)([], []); +}; +Media.prototype.permute = function (arr) { + if (arr.length === 0) { + return []; + } else if (arr.length === 1) { + return arr[0]; + } else { + var result = []; + var rest = this.permute(arr.slice(1)); + for (var i = 0; i < rest.length; i++) { + for (var j = 0; j < arr[0].length; j++) { + result.push([arr[0][j]].concat(rest[i])); } - return result; } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } + return result; + } }; -return Media; +Media.prototype.bubbleSelectors = function (selectors) { + if (!selectors) + return; + this.rules = [new(Ruleset)(selectors.slice(0), [this.rules[0]])]; }; +module.exports = Media; diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js index e8faebf49..e577b8767 100644 --- a/lib/less/tree/mixin-call.js +++ b/lib/less/tree/mixin-call.js @@ -1,153 +1,154 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Selector = require("./selector.js"), + MixinDefinition = require("./mixin-definition.js"), + defaultFunc = require("../functions/default.js"); -var Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); +var MixinCall = function (elements, args, index, currentFileInfo, important) { + this.selector = new(Selector)(elements); this.arguments = (args && args.length) ? args : null; this.index = index; this.currentFileInfo = currentFileInfo; this.important = important; }; -Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; +MixinCall.prototype = new Node(); +MixinCall.prototype.type = "MixinCall"; +MixinCall.prototype.accept = function (visitor) { + if (this.selector) { + this.selector = visitor.visit(this.selector); + } + if (this.arguments) { + this.arguments = visitor.visitArray(this.arguments); + } +}; +MixinCall.prototype.eval = function (env) { + var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, + candidates = [], candidate, conditionResult = [], + defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); + args = this.arguments && this.arguments.map(function (a) { + return { name: a.name, value: a.value.eval(env) }; + }); - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; + for (i = 0; i < env.frames.length; i++) { + if ((mixins = env.frames[i].find(this.selector)).length > 0) { + isOneFound = true; - // 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. + // 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. - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; + for (m = 0; m < mixins.length; m++) { + mixin = mixins[m]; + isRecursive = false; + for(f = 0; f < env.frames.length; f++) { + if ((!(mixin instanceof MixinDefinition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { + isRecursive = true; + break; } + } + if (isRecursive) { + continue; + } - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; + if (mixin.matchArgs(args, env)) { + candidate = {mixin: mixin, group: defNone}; - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - defaultFunc.value(f); - conditionResult[f] = mixin.matchCondition(args, env); + if (mixin.matchCondition) { + for (f = 0; f < 2; f++) { + 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; } - if (conditionResult[0] || conditionResult[1]) { - if (conditionResult[0] != conditionResult[1]) { - candidate.group = conditionResult[1] ? - defTrue : defFalse; - } - candidates.push(candidate); - } - } - else { candidates.push(candidate); } - - match = true; } + else { + candidates.push(candidate); + } + + match = true; } + } - defaultFunc.reset(); + defaultFunc.reset(); - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } + count = [0, 0, 0]; + for (m = 0; m < candidates.length; m++) { + count[candidates[m].group]++; + } - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } + if (count[defNone] > 0) { + defaultResult = defFalse; + } else { + defaultResult = defTrue; + if ((count[defTrue] + count[defFalse]) > 1) { + throw { type: 'Runtime', + message: 'Ambiguous use of `default()` found when matching for `' + + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; } + } - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; + for (m = 0; m < candidates.length; m++) { + candidate = candidates[m].group; + if ((candidate === defNone) || (candidate === defaultResult)) { + try { + mixin = candidates[m].mixin; + if (!(mixin instanceof MixinDefinition)) { + originalRuleset = mixin.originalRuleset || mixin; + mixin = new MixinDefinition("", [], mixin.rules, null, false); + mixin.originalRuleset = originalRuleset; } + Array.prototype.push.apply( + rules, mixin.evalCall(env, args, this.important).rules); + } catch (e) { + throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; } } + } - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } + if (match) { + if (!this.currentFileInfo || !this.currentFileInfo.reference) { + for (i = 0; i < rules.length; i++) { + rule = rules[i]; + if (rule.markReferenced) { + rule.markReferenced(); } } - return rules; } + return rules; } } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; + } + if (isOneFound) { + throw { type: 'Runtime', + message: 'No matching definition was found for `' + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } else { + throw { type: 'Name', + message: this.selector.toCSS().trim() + " is undefined", + index: this.index, filename: this.currentFileInfo.filename }; } }; -return Call; +MixinCall.prototype.format = function (args) { + return this.selector.toCSS().trim() + '(' + + (args ? args.map(function (a) { + var argValue = ""; + if (a.name) { + argValue += a.name + ":"; + } + if (a.value.toCSS) { + argValue += a.value.toCSS(); + } else { + argValue += "???"; + } + return argValue; + }).join(', ') : "") + ")"; }; +module.exports = MixinCall; diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js index 7a891d6bc..044250bf3 100644 --- a/lib/less/tree/mixin-definition.js +++ b/lib/less/tree/mixin-definition.js @@ -1,8 +1,13 @@ -module.exports = function (tree) { +var Selector = require("./selector.js"), + Element = require("./element.js"), + Ruleset = require("./ruleset.js"), + Rule = require("./rule.js"), + Expression = require("./expression.js"), + contexts = require("../env.js"); var Definition = function (name, params, rules, condition, variadic, frames) { this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; + this.selectors = [new(Selector)([new(Element)(null, name, this.index, this.currentFileInfo)])]; this.params = params; this.condition = condition; this.variadic = variadic; @@ -13,153 +18,146 @@ var Definition = function (name, params, rules, condition, variadic, frames) { if (!p.name || (p.name && !p.value)) { return count + 1; } else { return count; } }, 0); - this.parent = tree.Ruleset.prototype; this.frames = frames; }; -Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; +Definition.prototype = new Ruleset(); +Definition.prototype.type = "MixinDefinition"; +Definition.prototype.evalFirst = true; +Definition.prototype.accept = function (visitor) { + if (this.params && this.params.length) { + this.params = visitor.visitArray(this.params); + } + this.rules = visitor.visitArray(this.rules); + if (this.condition) { + this.condition = visitor.visit(this.condition); + } +}; +Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) { + /*jshint boss:true */ + var frame = new(Ruleset)(null, null), + varargs, arg, + params = this.params.slice(0), + i, j, val, name, isNamedFound, argIndex, argsLength = 0; + + mixinEnv = new contexts.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); + + if (args) { + args = args.slice(0); + argsLength = args.length; + + for(i = 0; i < argsLength; i++) { + arg = args[i]; + if (name = (arg && arg.name)) { + isNamedFound = false; + for(j = 0; j < params.length; j++) { + if (!evaldArguments[j] && name === params[j].name) { + evaldArguments[j] = arg.value.eval(env); + frame.prependRule(new(Rule)(name, arg.value.eval(env))); + isNamedFound = true; + break; } } + if (isNamedFound) { + args.splice(i, 1); + i--; + continue; + } else { + throw { type: 'Runtime', message: "Named argument for " + this.name + + ' ' + args[i].name + ' not found' }; + } } } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } + } + argIndex = 0; + for (i = 0; i < params.length; i++) { + if (evaldArguments[i]) { continue; } - arg = args && args[argIndex]; + arg = args && args[argIndex]; - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); + if (name = params[i].name) { + if (params[i].variadic) { + varargs = []; + for (j = argIndex; j < argsLength; j++) { + varargs.push(args[j].value.eval(env)); + } + frame.prependRule(new(Rule)(name, new(Expression)(varargs).eval(env))); + } else { + val = arg && arg.value; + if (val) { + val = val.eval(env); + } else if (params[i].value) { + val = params[i].value.eval(mixinEnv); + frame.resetCache(); } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; + throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + + ' (' + argsLength + ' for ' + this.arity + ')' }; } - } - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } + frame.prependRule(new(Rule)(name, val)); + evaldArguments[i] = val; } - argIndex++; } - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; + if (params[i].variadic && args) { + for (j = argIndex; j < argsLength; j++) { + evaldArguments[j] = args[j].value.eval(env); + } + } + argIndex++; + } - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); + return frame; +}; +Definition.prototype.eval = function (env) { + return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); +}; +Definition.prototype.evalCall = function (env, args, important) { + var _arguments = [], + mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, + frame = this.evalParams(env, new(contexts.evalEnv)(env, mixinFrames), args, _arguments), + rules, ruleset; - rules = this.rules.slice(0); + frame.prependRule(new(Rule)('@arguments', new(Expression)(_arguments).eval(env))); - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; + rules = this.rules.slice(0); - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } + ruleset = new(Ruleset)(null, rules); + ruleset.originalRuleset = this; + ruleset = ruleset.eval(new(contexts.evalEnv)(env, [this, frame].concat(mixinFrames))); + if (important) { + ruleset = this.makeImportant.apply(ruleset); + } + return ruleset; +}; +Definition.prototype.matchCondition = function (args, env) { + if (this.condition && !this.condition.eval( + new(contexts.evalEnv)(env, + [this.evalParams(env, new(contexts.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables + .concat(this.frames) // the parent namespace/mixin frames + .concat(env.frames)))) { // the current environment frames + return false; + } + return true; +}; +Definition.prototype.matchArgs = function (args, env) { + var argsLength = (args && args.length) || 0, len; + + if (! this.variadic) { + if (argsLength < this.required) { return false; } + if (argsLength > this.params.length) { return false; } + } else { + if (argsLength < (this.required - 1)) { return false; } + } - len = Math.min(argsLength, this.arity); + len = Math.min(argsLength, this.arity); - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } + for (var i = 0; i < len; i++) { + if (!this.params[i].name && !this.params[i].variadic) { + if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { + return false; } } - return true; } + return true; }; -return Definition; -}; +module.exports = Definition; diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index 6f3e9af8d..d33a31579 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -1,24 +1,20 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Operation = require("./operation.js"), + Dimension = require("./dimension.js"); var Negative = function (node) { this.value = node; }; -Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(Negative)(this.value.eval(env)); - } +Negative.prototype = new Node(); +Negative.prototype.type = "Negative"; +Negative.prototype.genCSS = function (env, output) { + output.add('-'); + this.value.genCSS(env, output); }; -return Negative; +Negative.prototype.eval = function (env) { + if (env.isMathOn()) { + return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(env); + } + return new(Negative)(this.value.eval(env)); }; +module.exports = Negative; diff --git a/lib/less/tree/node.js b/lib/less/tree/node.js new file mode 100644 index 000000000..c6bdd5720 --- /dev/null +++ b/lib/less/tree/node.js @@ -0,0 +1,35 @@ +var Node = function() { +}; +Node.prototype.toCSS = function (env) { + var strs = []; + this.genCSS(env, { + add: function(chunk, fileInfo, index) { + strs.push(chunk); + }, + isEmpty: function () { + return strs.length === 0; + } + }); + return strs.join(''); +}; +Node.prototype.genCSS = function (env, output) { + output.add(this.value); +}; +Node.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Node.prototype.eval = function () { return this; }; +Node.prototype._operate = function (env, op, a, b) { + switch (op) { + case '+': return a + b; + case '-': return a - b; + case '*': return a * b; + case '/': return a / b; + } +}; +Node.prototype.fround = function(env, value) { + var precision = env && env.numPrecision; + //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... + return (precision == null) ? value : Number((value + 2e-16).toFixed(precision)); +}; +module.exports = Node; diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index 53dc37ba3..6b558e2c5 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -1,60 +1,48 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Color = require("./color.js"), + Dimension = require("./dimension.js"); var Operation = function (op, operands, isSpaced) { this.op = op.trim(); this.operands = operands; this.isSpaced = isSpaced; }; -Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } +Operation.prototype = new Node(); +Operation.prototype.type = "Operation"; +Operation.prototype.accept = function (visitor) { + this.operands = visitor.visit(this.operands); +}; +Operation.prototype.eval = function (env) { + var a = this.operands[0].eval(env), + b = this.operands[1].eval(env); - return a.operate(env, this.op, b); - } else { - return new(Operation)(this.op, [a, b], this.isSpaced); + if (env.isMathOn()) { + if (a instanceof Dimension && b instanceof Color) { + a = a.toColor(); } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); + if (b instanceof Dimension && a instanceof Color) { + b = b.toColor(); } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); + if (!a.operate) { + throw { type: "Operation", + message: "Operation on an invalid type" }; } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; -// todo move! -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; + return a.operate(env, this.op, b); + } else { + return new(Operation)(this.op, [a, b], this.isSpaced); } }; - -return Operation; - +Operation.prototype.genCSS = function (env, output) { + this.operands[0].genCSS(env, output); + if (this.isSpaced) { + output.add(" "); + } + output.add(this.op); + if (this.isSpaced) { + output.add(" "); + } + this.operands[1].genCSS(env, output); }; + +module.exports = Operation; diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index 5c7dca864..ab9b22aa4 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -1,22 +1,16 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Paren = function (node) { this.value = node; }; -Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(Paren)(this.value.eval(env)); - } +Paren.prototype = new Node(); +Paren.prototype.type = "Paren"; +Paren.prototype.genCSS = function (env, output) { + output.add('('); + this.value.genCSS(env, output); + output.add(')'); }; -return Paren; +Paren.prototype.eval = function (env) { + return new(Paren)(this.value.eval(env)); }; +module.exports = Paren; diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index b0d3805d1..c46f8d5df 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -1,4 +1,5 @@ -module.exports = function (tree) { +var JsEvalNode = require("./js-eval-node.js"), + Variable = require("./variable.js"); var Quoted = function (str, content, escaped, index, currentFileInfo) { this.escaped = escaped; @@ -7,50 +8,47 @@ var Quoted = function (str, content, escaped, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; }; -Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; +Quoted.prototype = new JsEvalNode(); +Quoted.prototype.type = "Quoted"; +Quoted.prototype.genCSS = function (env, output) { + if (!this.escaped) { + output.add(this.quote, this.currentFileInfo, this.index); + } + output.add(this.value); + if (!this.escaped) { + output.add(this.quote); + } +}; +Quoted.prototype.eval = function (env) { + var that = this; + var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { + return String(that.evaluateJavaScript(exp, env)); + }).replace(/@\{([\w-]+)\}/g, function (_, name) { + var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); + return (v instanceof Quoted) ? v.value : v.toCSS(); + }); + return new(Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); +}; +Quoted.prototype.compare = function (x) { + if (!x.toCSS) { + return -1; + } - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } + var left, right; - if (left === right) { - return 0; - } + // when comparing quoted strings allow the quote to differ + if (x.type === "Quoted" && !this.escaped && !x.escaped) { + left = x.value; + right = this.value; + } else { + left = this.toCSS(); + right = x.toCSS(); + } - return left < right ? -1 : 1; + if (left === right) { + return 0; } + + return left < right ? -1 : 1; }; -return Quoted; -}; +module.exports = Quoted; diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index 8dad86338..ec09dcf58 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -1,8 +1,10 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Value = require("./value.js"), + Keyword = require("./keyword.js"); var Rule = function (name, value, important, merge, index, currentFileInfo, inline) { this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); + this.value = (value instanceof Node) ? value : new(Value)([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ?? this.important = important ? ' ' + important.trim() : ''; this.merge = merge; this.index = index; @@ -11,73 +13,6 @@ var Rule = function (name, value, important, merge, index, currentFileInfo, inli this.variable = name.charAt && (name.charAt(0) === '@'); }; -Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - function evalName(env, name) { var value = "", i, n = name.length, output = {add: function (s) {value += s;}}; @@ -86,7 +21,67 @@ function evalName(env, name) { } return value; } - -return Rule; +Rule.prototype = new Node(); +Rule.prototype.type = "Rule"; +Rule.prototype.genCSS = function (env, output) { + output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); + try { + this.value.genCSS(env, output); + } + catch(e) { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + throw e; + } + output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); }; +Rule.prototype.eval = function (env) { + var strictMathBypass = false, name = this.name, evaldValue; + if (typeof name !== "string") { + // expand 'primitive' name directly to get + // things faster (~10% for benchmark.less): + name = (name.length === 1) + && (name[0] instanceof Keyword) + ? name[0].value : evalName(env, name); + } + if (name === "font" && !env.strictMath) { + strictMathBypass = true; + env.strictMath = true; + } + try { + evaldValue = this.value.eval(env); + + if (!this.variable && evaldValue.type === "DetachedRuleset") { + throw { message: "Rulesets cannot be evaluated on a property.", + index: this.index, filename: this.currentFileInfo.filename }; + } + + return new(Rule)(name, + evaldValue, + this.important, + this.merge, + this.index, this.currentFileInfo, this.inline); + } + catch(e) { + if (typeof e.index !== 'number') { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + } + throw e; + } + finally { + if (strictMathBypass) { + env.strictMath = false; + } + } +}; +Rule.prototype.makeImportant = function () { + return new(Rule)(this.name, + this.value, + "!important", + this.merge, + this.index, this.currentFileInfo, this.inline); +}; + +module.exports = Rule; diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js index e7d998c43..537a97836 100644 --- a/lib/less/tree/ruleset-call.js +++ b/lib/less/tree/ruleset-call.js @@ -1,16 +1,13 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Variable = require("./variable.js"); var RulesetCall = function (variable) { this.variable = variable; }; -RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; -return RulesetCall; +RulesetCall.prototype = new Node(); +RulesetCall.prototype.type = "RulesetCall"; +RulesetCall.prototype.eval = function (env) { + var detachedRuleset = new(Variable)(this.variable).eval(env); + return detachedRuleset.callEval(env); }; +module.exports = RulesetCall; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 92337cf73..4c2ecf919 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -1,4 +1,10 @@ -module.exports = function (tree) { +var Node = require("./node.js"), + Rule = require("./rule.js"), + Selector = require("./selector.js"), + Element = require("./element.js"), + contexts = require("../env.js"), + defaultFunc = require("../functions/default.js"), + getDebugInfo = require("./debug-info.js"); var Ruleset = function (selectors, rules, strictImports) { this.selectors = selectors; @@ -6,575 +12,566 @@ var Ruleset = function (selectors, rules, strictImports) { this._lookups = {}; this.strictImports = strictImports; }; -Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } +Ruleset.prototype = new Node(); +Ruleset.prototype.type = "Ruleset"; +Ruleset.prototype.isRuleset = true; +Ruleset.prototype.isRulesetLike = true; +Ruleset.prototype.accept = function (visitor) { + if (this.paths) { + visitor.visitArray(this.paths, true); + } else if (this.selectors) { + this.selectors = visitor.visitArray(this.selectors); + } + if (this.rules && this.rules.length) { + this.rules = visitor.visitArray(this.rules); + } +}; +Ruleset.prototype.eval = function (env) { + var thisSelectors = this.selectors, selectors, + selCnt, selector, i, hasOnePassingSelector = false; + + if (thisSelectors && (selCnt = thisSelectors.length)) { + selectors = []; + defaultFunc.error({ + type: "Syntax", + message: "it is currently only allowed in parametric mixin guards," + }); + for (i = 0; i < selCnt; i++) { + selector = thisSelectors[i].eval(env); + selectors.push(selector); + if (selector.evaldCondition) { + hasOnePassingSelector = true; } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; } + defaultFunc.reset(); + } else { + hasOnePassingSelector = true; + } - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(Ruleset)(selectors, rules, this.strictImports), - rule, subRule; + var rules = this.rules ? this.rules.slice(0) : null, + ruleset = new(Ruleset)(selectors, rules, this.strictImports), + rule, subRule; - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; + ruleset.originalRuleset = this; + ruleset.root = this.root; + ruleset.firstRoot = this.firstRoot; + ruleset.allowImports = this.allowImports; - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } + if(this.debugInfo) { + ruleset.debugInfo = this.debugInfo; + } - if (!hasOnePassingSelector) { - rules.length = 0; - } + if (!hasOnePassingSelector) { + rules.length = 0; + } - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); + // push the current ruleset to the frames stack + var envFrames = env.frames; + envFrames.unshift(ruleset); - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); + // currrent selectors + var envSelectors = env.selectors; + if (!envSelectors) { + env.selectors = envSelectors = []; + } + envSelectors.unshift(this.selectors); - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } + // Evaluate imports + if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { + ruleset.evalImports(env); + } - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } + // Store the frames around mixin definitions, + // so they can be evaluated like closures when the time comes. + var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; + for (i = 0; i < rsRuleCnt; i++) { + if (rsRules[i].evalFirst) { + rsRules[i] = rsRules[i].eval(env); } + } - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } + var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; + + // Evaluate mixin calls. + for (i = 0; i < rsRuleCnt; i++) { + if (rsRules[i].type === "MixinCall") { + /*jshint loopfunc:true */ + rules = rsRules[i].eval(env).filter(function(r) { + if ((r instanceof 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 + return !(ruleset.variable(r.name)); + } + return true; + }); + rsRules.splice.apply(rsRules, [i, 1].concat(rules)); + rsRuleCnt += rules.length - 1; + i += rules.length-1; + ruleset.resetCache(); + } else if (rsRules[i].type === "RulesetCall") { + /*jshint loopfunc:true */ + rules = rsRules[i].eval(env).rules.filter(function(r) { + if ((r instanceof Rule) && r.variable) { + // do not pollute the scope at all + return false; + } + return true; + }); + rsRules.splice.apply(rsRules, [i, 1].concat(rules)); + rsRuleCnt += rules.length - 1; + i += rules.length-1; + ruleset.resetCache(); } + } - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } + // Evaluate everything else + for (i = 0; i < rsRules.length; i++) { + rule = rsRules[i]; + if (!rule.evalFirst) { + rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; } + } - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } + // Evaluate everything else + for (i = 0; i < rsRules.length; i++) { + rule = rsRules[i]; + // for rulesets, check if it is a css guard and can be removed + if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) { + // check if it can be folded in (e.g. & where) + if (rule.selectors[0].isJustParentSelector()) { + rsRules.splice(i--, 1); + + for(var j = 0; j < rule.rules.length; j++) { + subRule = rule.rules[j]; + if (!(subRule instanceof Rule) || !subRule.variable) { + rsRules.splice(++i, 0, subRule); } } } } + } - // Pop the stack - envFrames.shift(); - envSelectors.shift(); + // Pop the stack + envFrames.shift(); + envSelectors.shift(); - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } + if (env.mediaBlocks) { + for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { + env.mediaBlocks[i].bubbleSelectors(selectors); } + } - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); + return ruleset; +}; +Ruleset.prototype.evalImports = function(env) { + var rules = this.rules, i, importRules; + if (!rules) { return; } + + for (i = 0; i < rules.length; i++) { + if (rules[i].type === "Import") { + importRules = rules[i].eval(env); + if (importRules && importRules.length) { + rules.splice.apply(rules, [i, 1].concat(importRules)); + i+= importRules.length-1; + } else { + rules.splice(i, 1, importRules); } + this.resetCache(); } - }, - makeImportant: function() { - return new Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; + } +}; +Ruleset.prototype.makeImportant = function() { + return new Ruleset(this.selectors, this.rules.map(function (r) { + if (r.makeImportant) { + return r.makeImportant(); + } else { + return r; } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); + }), this.strictImports); +}; +Ruleset.prototype.matchArgs = function (args) { + return !args || args.length === 0; +}; +// lets you call a css selector with a guard +Ruleset.prototype.matchCondition = function (args, env) { + var lastSelector = this.selectors[this.selectors.length-1]; + if (!lastSelector.evaldCondition) { + return false; + } + if (lastSelector.condition && + !lastSelector.condition.eval( + new(contexts.evalEnv)(env, + env.frames))) { + return false; + } + return true; +}; +Ruleset.prototype.resetCache = function () { + this._rulesets = null; + this._variables = null; + this._lookups = {}; +}; +Ruleset.prototype.variables = function () { + if (!this._variables) { + this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { + if (r instanceof Rule && r.variable === true) { + hash[r.name] = r; } + return hash; + }, {}); + } + return this._variables; +}; +Ruleset.prototype.variable = function (name) { + return this.variables()[name]; +}; +Ruleset.prototype.rulesets = function () { + if (!this.rules) { return null; } + + var filtRules = [], rules = this.rules, cnt = rules.length, + i, rule; + + for (i = 0; i < cnt; i++) { + rule = rules[i]; + if (rule.isRuleset) { + filtRules.push(rule); } + } - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; + return filtRules; +}; +Ruleset.prototype.prependRule = function (rule) { + var rules = this.rules; + if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } +}; +Ruleset.prototype.find = function (selector, self) { + self = self || this; + var rules = [], match, + key = selector.toCSS(); + + if (key in this._lookups) { return this._lookups[key]; } + + this.rulesets().forEach(function (rule) { + if (rule !== self) { + for (var j = 0; j < rule.selectors.length; j++) { + match = selector.match(rule.selectors[j]); + if (match) { + if (selector.elements.length > match) { + Array.prototype.push.apply(rules, rule.find( + new(Selector)(selector.elements.slice(match)), self)); + } else { + rules.push(rule); } + break; } } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - charsetRuleNodes = [], - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; } + }); + this._lookups[key] = rules; + return rules; +}; +Ruleset.prototype.genCSS = function (env, output) { + var i, j, + charsetRuleNodes = [], + ruleNodes = [], + rulesetNodes = [], + rulesetNodeCnt, + debugInfo, // Line number debugging + rule, + path; + + env.tabLevel = (env.tabLevel || 0); + + if (!this.root) { + env.tabLevel++; + } - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - function isRulesetLikeNode(rule, root) { - // if it has nested rules, then it should be treated like a ruleset - if (rule.rules) - return true; - - // medias and comments do not have nested rules, but should be treated like rulesets anyway - if ( (rule instanceof tree.Media) || (root && rule instanceof tree.Comment)) - return true; - - // some directives and anonumoust nodes are ruleset like, others are not - if ((rule instanceof tree.Directive) || (rule instanceof tree.Anonymous)) { - return rule.isRulesetLike(); - } - - //anything else is assumed to be a rule - return false; - } + var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), + tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), + sep; + + function isRulesetLikeNode(rule, root) { + // if it has nested rules, then it should be treated like a ruleset + // medias and comments do not have nested rules, but should be treated like rulesets anyway + // some directives and anonymous nodes are ruleset like, others are not + if (typeof rule.isRulesetLike === "boolean") + { + return rule.isRulesetLike; + } else if (typeof rule.isRulesetLike === "function") + { + return rule.isRulesetLike(root); + } + + //anything else is assumed to be a rule + return false; + } - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (isRulesetLikeNode(rule, this.root)) { - rulesetNodes.push(rule); + for (i = 0; i < this.rules.length; i++) { + rule = this.rules[i]; + if (isRulesetLikeNode(rule, this.root)) { + rulesetNodes.push(rule); + } else { + //charsets should float on top of everything + if (rule.isCharset && rule.isCharset()) { + charsetRuleNodes.push(rule); } else { - //charsets should float on top of everything - if (rule.isCharset && rule.isCharset()) { - charsetRuleNodes.push(rule); - } else { - ruleNodes.push(rule); - } + ruleNodes.push(rule); } } - ruleNodes = charsetRuleNodes.concat(ruleNodes); + } + ruleNodes = charsetRuleNodes.concat(ruleNodes); - // If this is the root node, we don't render - // a selector, or {}. - if (!this.root) { - debugInfo = tree.debugInfo(env, this, tabSetStr); + // If this is the root node, we don't render + // a selector, or {}. + if (!this.root) { + debugInfo = getDebugInfo(env, this, tabSetStr); - if (debugInfo) { - output.add(debugInfo); - output.add(tabSetStr); - } + if (debugInfo) { + output.add(debugInfo); + output.add(tabSetStr); + } - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; + var paths = this.paths, pathCnt = paths.length, + pathSubCnt; - sep = env.compress ? ',' : (',\n' + tabSetStr); + sep = env.compress ? ',' : (',\n' + tabSetStr); - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } + for (i = 0; i < pathCnt; i++) { + path = paths[i]; + if (!(pathSubCnt = path.length)) { continue; } + if (i > 0) { output.add(sep); } - env.firstSelector = true; - path[0].genCSS(env, output); + env.firstSelector = true; + path[0].genCSS(env, output); - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } + env.firstSelector = false; + for (j = 1; j < pathSubCnt; j++) { + path[j].genCSS(env, output); } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); } - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; i++) { - rule = ruleNodes[i]; + output.add((env.compress ? '{' : ' {\n') + tabRuleStr); + } - // @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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } + // Compile rules and rulesets + for (i = 0; i < ruleNodes.length; i++) { + rule = ruleNodes[i]; - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } + // @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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { + env.lastRule = true; } - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; + if (rule.genCSS) { + rule.genCSS(env, output); + } else if (rule.value) { + output.add(rule.value.toString()); } - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } + if (!env.lastRule) { + output.add(env.compress ? '' : ('\n' + tabRuleStr)); + } else { + env.lastRule = false; } + } - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); + if (!this.root) { + output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); + env.tabLevel--; + } + + sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); + rulesetNodeCnt = rulesetNodes.length; + if (rulesetNodeCnt) { + if (ruleNodes.length && sep) { output.add(sep); } + rulesetNodes[0].genCSS(env, output); + for (i = 1; i < rulesetNodeCnt; i++) { + if (sep) { output.add(sep); } + rulesetNodes[i].genCSS(env, output); } - }, + } - toCSS: tree.toCSS, + if (!output.isEmpty() && !env.compress && this.firstRoot) { + output.add('\n'); + } +}; +Ruleset.prototype.markReferenced = function () { + if (!this.selectors) { + return; + } + for (var s = 0; s < this.selectors.length; s++) { + this.selectors[s].markReferenced(); + } +}; +Ruleset.prototype.joinSelectors = function (paths, context, selectors) { + for (var s = 0; s < selectors.length; s++) { + this.joinSelector(paths, context, selectors[s]); + } +}; +Ruleset.prototype.joinSelector = function (paths, context, selector) { - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, + var i, j, k, + hasParentSelector, newSelectors, el, sel, parentSel, + newSelectorPath, afterParentJoin, newJoinedSelector, + newJoinedSelectorEmpty, lastSelector, currentElements, + selectorsMultiplied; - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); + for (i = 0; i < selector.elements.length; i++) { + el = selector.elements[i]; + if (el.value === '&') { + hasParentSelector = true; } - }, + } - joinSelector: function (paths, context, selector) { + if (!hasParentSelector) { + if (context.length > 0) { + for (i = 0; i < context.length; i++) { + paths.push(context[i].concat(selector)); + } + } + else { + paths.push([selector]); + } + return; + } - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; + // 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 = []; + // 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 = [[]]; + + for (i = 0; i < selector.elements.length; i++) { + el = selector.elements[i]; + // non parent reference elements just get added + if (el.value !== "&") { + currentElements.push(el); + } else { + // the new list of selectors to add + selectorsMultiplied = []; - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; + // merge the current list of non parent selector elements + // on to the current list of selectors to add + if (currentElements.length > 0) { + this.mergeElementsOnToSelectors(currentElements, newSelectors); } - } - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); + // loop through our current selectors + for (j = 0; j < newSelectors.length; j++) { + sel = newSelectors[j]; + // 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.length === 0) { + // the combinator used on el should now be applied to the next element instead so that + // it is not lost + if (sel.length > 0) { + sel[0].elements = sel[0].elements.slice(0); + sel[0].elements.push(new(Element)(el.combinator, '', el.index, el.currentFileInfo)); + } + selectorsMultiplied.push(sel); } - } - else { - paths.push([selector]); - } - return; - } + else { + // and the parent selectors + for (k = 0; k < context.length; k++) { + parentSel = context[k]; + // We need to put the current selectors + // then join the last selector's elements on to the parents selectors + + // our new selector path + newSelectorPath = []; + // selectors from the parent after the join + afterParentJoin = []; + 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.length > 0) { + newSelectorPath = sel.slice(0); + lastSelector = newSelectorPath.pop(); + newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); + newJoinedSelectorEmpty = false; + } + else { + newJoinedSelector = selector.createDerived([]); + } - // 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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; + //put together the parent selectors after the join + if (parentSel.length > 1) { + afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); + } - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } + if (parentSel.length > 0) { + newJoinedSelectorEmpty = false; - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); + // join the elements so far with the first part of the parent + newJoinedSelector.elements.push(new(Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); + newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); + + if (!newJoinedSelectorEmpty) { + // now add the joined selector + newSelectorPath.push(newJoinedSelector); } + + // and the rest of the parent + newSelectorPath = newSelectorPath.concat(afterParentJoin); + + // add that to our new set of selectors + selectorsMultiplied.push(newSelectorPath); } } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; } - } - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); + // our new selectors has been multiplied, so reset the state + newSelectors = selectorsMultiplied; + currentElements = []; } + } - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; + // if we have any elements left over (e.g. .a& .b == .b) + // add them on to all the current selectors + if (currentElements.length > 0) { + this.mergeElementsOnToSelectors(currentElements, newSelectors); + } - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; + for (i = 0; i < newSelectors.length; i++) { + if (newSelectors[i].length > 0) { + paths.push(newSelectors[i]); } + } +}; +Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) { + var i, sel; - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; + if (selectors.length === 0) { + selectors.push([ new(Selector)(elements) ]); + return; + } - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } + for (i = 0; i < selectors.length; i++) { + sel = selectors[i]; + + // if the previous thing in sel is a parent this needs to join on to it + if (sel.length > 0) { + sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); + } + else { + sel.push(new(Selector)(elements)); } } }; -return Ruleset; -}; +module.exports = Ruleset; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index 75d10e356..ef39ece1b 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -1,4 +1,4 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { this.elements = elements; @@ -10,120 +10,117 @@ var Selector = function (elements, extendList, condition, index, currentFileInfo this.evaldCondition = true; } }; -Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; +Selector.prototype = new Node(); +Selector.prototype.type = "Selector"; +Selector.prototype.accept = function (visitor) { + if (this.elements) { + this.elements = visitor.visitArray(this.elements); + } + if (this.extendList) { + this.extendList = visitor.visitArray(this.extendList); + } + if (this.condition) { + this.condition = visitor.visit(this.condition); + } +}; +Selector.prototype.createDerived = function(elements, extendList, evaldCondition) { + evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; + var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); + newSelector.evaldCondition = evaldCondition; + newSelector.mediaEmpty = this.mediaEmpty; + return newSelector; +}; +Selector.prototype.match = function (other) { + var elements = this.elements, + len = elements.length, + olen, i; - other.CacheElements(); + other.CacheElements(); - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } + olen = other._elements.length; + if (olen === 0 || len < olen) { + return 0; + } else { + for (i = 0; i < olen; i++) { + if (elements[i].value !== other._elements[i]) { + return 0; } } + } - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ + return olen; // return number of matched elements +}; +Selector.prototype.CacheElements = function(){ + var css = '', len, v, i; - len = this.elements.length; - for(i = 0; i < len; i++){ + if( !this._elements ){ - v = this.elements[i]; - css += v.combinator.value; + len = this.elements.length; + for(i = 0; i < len; i++){ - if( !v.value.value ){ - css += v.value; - continue; - } + v = this.elements[i]; + css += v.combinator.value; - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; + if( !v.value.value ){ + css += v.value; + continue; } - this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); + if( typeof v.value.value !== "string" ){ + css = ''; + break; + } + css += v.value.value; + } - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } + this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); - } else { - this._elements = []; + if (this._elements) { + if (this._elements[0] === "&") { + this._elements.shift(); } + } else { + this._elements = []; } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); + } +}; +Selector.prototype.isJustParentSelector = function() { + return !this.mediaEmpty && + this.elements.length === 1 && + this.elements[0].value === '&' && + (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); +}; +Selector.prototype.eval = function (env) { + var evaldCondition = this.condition && this.condition.eval(env), + elements = this.elements, extendList = this.extendList; - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } + elements = elements && elements.map(function (e) { return e.eval(env); }); + extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); + + return this.createDerived(elements, extendList, evaldCondition); +}; +Selector.prototype.genCSS = function (env, output) { + var i, element; + if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { + output.add(' ', this.currentFileInfo, this.index); + } + if (!this._css) { + //TODO caching? speed comparison? + for(i = 0; i < this.elements.length; i++) { + element = this.elements[i]; + element.genCSS(env, output); } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; } }; -return Selector; +Selector.prototype.markReferenced = function () { + this.isReferenced = true; +}; +Selector.prototype.getIsReferenced = function() { + return !this.currentFileInfo.reference || this.isReferenced; +}; +Selector.prototype.getIsOutput = function() { + return this.evaldCondition; }; +module.exports = Selector; diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js index 674ac9830..72f33ea80 100644 --- a/lib/less/tree/unicode-descriptor.js +++ b/lib/less/tree/unicode-descriptor.js @@ -1,15 +1,9 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var UnicodeDescriptor = function (value) { this.value = value; }; -UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; -return UnicodeDescriptor; -}; +UnicodeDescriptor.prototype = new Node(); +UnicodeDescriptor.prototype.type = "UnicodeDescriptor"; + +module.exports = UnicodeDescriptor; diff --git a/lib/less/tree/unit.js b/lib/less/tree/unit.js index c25a7173a..9589498ea 100644 --- a/lib/less/tree/unit.js +++ b/lib/less/tree/unit.js @@ -1,4 +1,5 @@ -module.exports = function(tree, unitConversions) { +var Node = require("./node.js"), + unitConversions = require("../data/unit-conversions.js"); var Unit = function (numerator, denominator, backupUnit) { this.numerator = numerator ? numerator.slice(0).sort() : []; @@ -6,132 +7,120 @@ var Unit = function (numerator, denominator, backupUnit) { this.backupUnit = backupUnit; }; -Unit.prototype = { - type: "Unit", - clone: function () { - return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, +Unit.prototype = new Node(); +Unit.prototype.type = "Unit"; +Unit.prototype.clone = function () { + return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); +}; +Unit.prototype.genCSS = function (env, output) { + if (this.numerator.length >= 1) { + output.add(this.numerator[0]); + } else + if (this.denominator.length >= 1) { + output.add(this.denominator[0]); + } else + if ((!env || !env.strictUnits) && this.backupUnit) { + output.add(this.backupUnit); + } +}; +Unit.prototype.toString = function () { + var i, returnStr = this.numerator.join("*"); + for (i = 0; i < this.denominator.length; i++) { + returnStr += "/" + this.denominator[i]; + } + return returnStr; +}; +Unit.prototype.compare = function (other) { + return this.is(other.toString()) ? 0 : -1; +}; +Unit.prototype.is = function (unitString) { + return this.toString() === unitString; +}; +Unit.prototype.isLength = function () { + return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); +}; +Unit.prototype.isEmpty = function () { + return this.numerator.length === 0 && this.denominator.length === 0; +}; +Unit.prototype.isSingular = function() { + return this.numerator.length <= 1 && this.denominator.length === 0; +}; +Unit.prototype.map = function(callback) { + var i; - map: function(callback) { - var i; + for (i = 0; i < this.numerator.length; i++) { + this.numerator[i] = callback(this.numerator[i], false); + } - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } + for (i = 0; i < this.denominator.length; i++) { + this.denominator[i] = callback(this.denominator[i], true); + } +}; +Unit.prototype.usedUnits = function() { + var group, result = {}, mapUnit; - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); + mapUnit = function (atomicUnit) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { + result[groupName] = atomicUnit; } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - return atomicUnit; - }; + return atomicUnit; + }; - for (var groupName in unitConversions) { - if (unitConversions.hasOwnProperty(groupName)) { - group = unitConversions[groupName]; + for (var groupName in unitConversions) { + if (unitConversions.hasOwnProperty(groupName)) { + group = unitConversions[groupName]; - this.map(mapUnit); - } + this.map(mapUnit); } + } - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; + return result; +}; +Unit.prototype.cancel = function () { + var counter = {}, atomicUnit, i, backup; - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; + for (i = 0; i < this.numerator.length; i++) { + atomicUnit = this.numerator[i]; + if (!backup) { + backup = atomicUnit; } + counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; + } - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; + for (i = 0; i < this.denominator.length; i++) { + atomicUnit = this.denominator[i]; + if (!backup) { + backup = atomicUnit; } + counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; + } + + this.numerator = []; + this.denominator = []; + + for (atomicUnit in counter) { + if (counter.hasOwnProperty(atomicUnit)) { + var count = counter[atomicUnit]; - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } + if (count > 0) { + for (i = 0; i < count; i++) { + this.numerator.push(atomicUnit); + } + } else if (count < 0) { + for (i = 0; i < -count; i++) { + this.denominator.push(atomicUnit); } } } + } - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); + if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { + this.backupUnit = backup; } + + this.numerator.sort(); + this.denominator.sort(); }; -return Unit; -}; \ No newline at end of file +module.exports = Unit; diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 811c40765..603e4b71c 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -1,53 +1,50 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var URL = function (val, currentFileInfo, isEvald) { this.value = val; this.currentFileInfo = currentFileInfo; this.isEvald = isEvald; }; -URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; +URL.prototype = new Node(); +URL.prototype.type = "Url"; +URL.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +URL.prototype.genCSS = function (env, output) { + output.add("url("); + this.value.genCSS(env, output); + output.add(")"); +}; +URL.prototype.eval = function (ctx) { + var val = this.value.eval(ctx), + rootpath; - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; + if (!this.isEvald) { + // Add the base path if the URL is relative + rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; + if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { + if (!val.quote) { + rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); } + val.value = rootpath + val.value; + } - val.value = ctx.normalizePath(val.value); + val.value = ctx.normalizePath(val.value); - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } + // Add url args if enabled + if (ctx.urlArgs) { + if (!val.value.match(/^\s*data:/)) { + var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; + var urlArgs = delimiter + ctx.urlArgs; + if (val.value.indexOf('#') !== -1) { + val.value = val.value.replace('#', urlArgs + '#'); + } else { + val.value += urlArgs; } } } - - return new(URL)(val, this.currentFileInfo, true); } + + return new(URL)(val, this.currentFileInfo, true); }; -return URL; -}; +module.exports = URL; diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index ea07367b7..fb1c7f850 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -1,34 +1,31 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Value = function (value) { this.value = value; }; -Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS +Value.prototype = new Node(); +Value.prototype.type = "Value"; +Value.prototype.accept = function (visitor) { + if (this.value) { + this.value = visitor.visitArray(this.value); + } }; -return Value; +Value.prototype.eval = function (env) { + if (this.value.length === 1) { + return this.value[0].eval(env); + } else { + return new(Value)(this.value.map(function (v) { + return v.eval(env); + })); + } +}; +Value.prototype.genCSS = function (env, output) { + var i; + for(i = 0; i < this.value.length; i++) { + this.value[i].genCSS(env, output); + if (i+1 < this.value.length) { + output.add((env && env.compress) ? ',' : ', '); + } + } }; +module.exports = Value; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 71c373fc6..85641a752 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -1,44 +1,49 @@ -module.exports = function (tree) { +var Node = require("./node.js"); var Variable = function (name, index, currentFileInfo) { this.name = name; this.index = index; this.currentFileInfo = currentFileInfo || {}; }; -Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; +Variable.prototype = new Node(); +Variable.prototype.type = "Variable"; +Variable.prototype.eval = function (env) { + var variable, name = this.name; - if (name.indexOf('@@') === 0) { - name = '@' + new(Variable)(name.slice(1)).eval(env).value; - } + if (name.indexOf('@@') === 0) { + name = '@' + new(Variable)(name.slice(1)).eval(env).value; + } - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } + if (this.evaluating) { + throw { type: 'Name', + message: "Recursive variable definition for " + name, + filename: this.currentFileInfo.file, + index: this.index }; + } - this.evaluating = true; + this.evaluating = true; - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; + variable = this.find(env.frames, function (frame) { + var v = frame.variable(name); + if (v) { + return v.value.eval(env); } + }); + if (variable) { + this.evaluating = false; + return variable; + } else { + throw { type: 'Name', + message: "variable " + name + " is undefined", + filename: this.currentFileInfo.filename, + index: this.index }; } }; -return Variable; +Variable.prototype.find = function (obj, fun) { + for (var i = 0, r; i < obj.length; i++) { + r = fun.call(obj, obj[i]); + if (r) { return r; } + } + return null; }; +module.exports = Variable; diff --git a/lib/less/visitor/import-visitor.js b/lib/less/visitor/import-visitor.js index 1dc19f2bc..2f1c24bd4 100644 --- a/lib/less/visitor/import-visitor.js +++ b/lib/less/visitor/import-visitor.js @@ -1,9 +1,10 @@ +var contexts = require("../env.js"); module.exports = function (visitor, tree) { var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { this._visitor = new visitor(this); this._importer = importer; this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); + this.env = evalEnv || new contexts.evalEnv(); this.importCount = 0; this.onceFileDetectionMap = onceFileDetectionMap || {}; this.recursionDetector = {}; @@ -54,7 +55,7 @@ module.exports = function (visitor, tree) { if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { importNode = evaldImportNode; this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); + var env = new contexts.evalEnv(this.env, this.env.frames.slice(0)); if (importNode.options.multiple) { env.importMultiple = true; From c41df95d23ed87e5754e3b993090a3de463bc6e6 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 18:01:34 +0100 Subject: [PATCH 050/161] move tree index underneath tree --- lib/less/data/index.js | 4 ++++ lib/less/non-node-index.js | 6 ++---- lib/less/tree.js | 40 -------------------------------------- lib/less/tree/index.js | 40 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 44 deletions(-) create mode 100644 lib/less/data/index.js delete mode 100644 lib/less/tree.js create mode 100644 lib/less/tree/index.js diff --git a/lib/less/data/index.js b/lib/less/data/index.js new file mode 100644 index 000000000..436caae88 --- /dev/null +++ b/lib/less/data/index.js @@ -0,0 +1,4 @@ +module.exports = { + colors: require("./colors.js"), + unitConversions: require("./unit-conversions.js") +}; diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 7a7e0f5fb..df25f9a8a 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -1,11 +1,9 @@ var less = { version: [1, 6, 3], - data: { - colors: require('./data/colors') - } + data: require('./data/index.js') }; -less.tree = require('./tree'); +less.tree = require('./tree/index.js'); less.visitor = require('./visitor/index.js')(less, less.tree); less.Parser = (require('./parser'))(less, less.tree, less.visitor); less.functions = (require('./functions/index.js'))(less, less.tree); diff --git a/lib/less/tree.js b/lib/less/tree.js deleted file mode 100644 index ed72dc472..000000000 --- a/lib/less/tree.js +++ /dev/null @@ -1,40 +0,0 @@ -var tree = {}; - -tree.Alpha = require('./tree/alpha'); -tree.Color = require('./tree/color'); -tree.Directive = require('./tree/directive'); -tree.DetachedRuleset = require('./tree/detached-ruleset'); -tree.Operation = require('./tree/operation'); -tree.Dimension = require('./tree/dimension'); -tree.Unit = require('./tree/unit'); -tree.Keyword = require('./tree/keyword'); -tree.Variable = require('./tree/variable'); -tree.Ruleset = require('./tree/ruleset'); -tree.Element = require('./tree/element'); -tree.Attribute = require('./tree/attribute'); -tree.Combinator = require('./tree/combinator'); -tree.Selector = require('./tree/selector'); -tree.Quoted = require('./tree/quoted'); -tree.Expression = require('./tree/expression'); -tree.Rule = require('./tree/rule'); -tree.Call = require('./tree/call'); -tree.URL = require('./tree/url'); -tree.Import = require('./tree/import'); -tree.mixin = { - Call: require('./tree/mixin-call'), - Definition: require('./tree/mixin-definition') -}; -tree.Comment = require('./tree/comment'); -tree.Anonymous = require('./tree/anonymous'); -tree.Value = require('./tree/value'); -tree.JavaScript = require('./tree/javascript'); -tree.Assignment = require('./tree/assignment'); -tree.Condition = require('./tree/condition'); -tree.Paren = require('./tree/paren'); -tree.Media = require('./tree/media'); -tree.UnicodeDescriptor = require('./tree/unicode-descriptor'); -tree.Negative = require('./tree/negative'); -tree.Extend = require('./tree/extend'); -tree.RulesetCall = require('./tree/ruleset-call'); - -module.exports = tree; diff --git a/lib/less/tree/index.js b/lib/less/tree/index.js new file mode 100644 index 000000000..a6004465f --- /dev/null +++ b/lib/less/tree/index.js @@ -0,0 +1,40 @@ +var tree = {}; + +tree.Alpha = require('./alpha'); +tree.Color = require('./color'); +tree.Directive = require('./directive'); +tree.DetachedRuleset = require('./detached-ruleset'); +tree.Operation = require('./operation'); +tree.Dimension = require('./dimension'); +tree.Unit = require('./unit'); +tree.Keyword = require('./keyword'); +tree.Variable = require('./variable'); +tree.Ruleset = require('./ruleset'); +tree.Element = require('./element'); +tree.Attribute = require('./attribute'); +tree.Combinator = require('./combinator'); +tree.Selector = require('./selector'); +tree.Quoted = require('./quoted'); +tree.Expression = require('./expression'); +tree.Rule = require('./rule'); +tree.Call = require('./call'); +tree.URL = require('./url'); +tree.Import = require('./import'); +tree.mixin = { + Call: require('./mixin-call'), + Definition: require('./mixin-definition') +}; +tree.Comment = require('./comment'); +tree.Anonymous = require('./anonymous'); +tree.Value = require('./value'); +tree.JavaScript = require('./javascript'); +tree.Assignment = require('./assignment'); +tree.Condition = require('./condition'); +tree.Paren = require('./paren'); +tree.Media = require('./media'); +tree.UnicodeDescriptor = require('./unicode-descriptor'); +tree.Negative = require('./negative'); +tree.Extend = require('./extend'); +tree.RulesetCall = require('./ruleset-call'); + +module.exports = tree; From 95dedcc576032b0dcdb98022960ed382112cabb2 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 18:03:33 +0100 Subject: [PATCH 051/161] rename env to contexts --- build/build.yml | 4 ++-- lib/less/{env.js => contexts.js} | 0 lib/less/non-node-index.js | 2 +- lib/less/parser.js | 2 +- lib/less/tree/detached-ruleset.js | 2 +- lib/less/tree/mixin-definition.js | 2 +- lib/less/tree/ruleset.js | 2 +- lib/less/visitor/import-visitor.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename lib/less/{env.js => contexts.js} (100%) diff --git a/build/build.yml b/build/build.yml index 0353c0fcb..14c084578 100644 --- a/build/build.yml +++ b/build/build.yml @@ -37,7 +37,7 @@ less: colors : <%= build.lib %>/colors.js tree : <%= build.lib %>/tree.js treedir : <%= build.lib %>/tree/*.js # glob all files in ./lib/less/tree directory - env : <%= build.lib %>/env.js + env : <%= build.lib %>/contexts.js visitor : <%= build.lib %>/visitor.js import_visitor : <%= build.lib %>/import-visitor.js join : <%= build.lib %>/join-selector-visitor.js @@ -127,4 +127,4 @@ tree: source_map: - <%= build.lib_source_map %>/source-map-header.js - <%= build.lib_source_map %>/source-map-0.1.31.js - - <%= build.lib_source_map %>/source-map-footer.js \ No newline at end of file + - <%= build.lib_source_map %>/source-map-footer.js diff --git a/lib/less/env.js b/lib/less/contexts.js similarity index 100% rename from lib/less/env.js rename to lib/less/contexts.js diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index df25f9a8a..2f5bcb020 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -7,7 +7,7 @@ less.tree = require('./tree/index.js'); less.visitor = require('./visitor/index.js')(less, less.tree); less.Parser = (require('./parser'))(less, less.tree, less.visitor); less.functions = (require('./functions/index.js'))(less, less.tree); -less.contexts = require("./env.js"); +less.contexts = require("./contexts.js"); less.tree.sourceMapOutput = require('./source-map-output.js')(less); diff --git a/lib/less/parser.js b/lib/less/parser.js index 61fcd9359..7a8744e28 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,6 +1,6 @@ var chunker = require('./chunker.js'); var LessError = require('./less-error.js'); -var contexts = require("./env.js"); +var contexts = require("./contexts.js"); module.exports = function(less, tree, visitor) { // diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js index 009d67c2e..4c7723f49 100644 --- a/lib/less/tree/detached-ruleset.js +++ b/lib/less/tree/detached-ruleset.js @@ -1,5 +1,5 @@ var Node = require("./node.js"), - contexts = require("../env.js"); + contexts = require("../contexts.js"); var DetachedRuleset = function (ruleset, frames) { this.ruleset = ruleset; diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js index 044250bf3..3c2f1950f 100644 --- a/lib/less/tree/mixin-definition.js +++ b/lib/less/tree/mixin-definition.js @@ -3,7 +3,7 @@ var Selector = require("./selector.js"), Ruleset = require("./ruleset.js"), Rule = require("./rule.js"), Expression = require("./expression.js"), - contexts = require("../env.js"); + contexts = require("../contexts.js"); var Definition = function (name, params, rules, condition, variadic, frames) { this.name = name; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 4c2ecf919..bc0c2627e 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -2,7 +2,7 @@ var Node = require("./node.js"), Rule = require("./rule.js"), Selector = require("./selector.js"), Element = require("./element.js"), - contexts = require("../env.js"), + contexts = require("../contexts.js"), defaultFunc = require("../functions/default.js"), getDebugInfo = require("./debug-info.js"); diff --git a/lib/less/visitor/import-visitor.js b/lib/less/visitor/import-visitor.js index 2f1c24bd4..efff072a3 100644 --- a/lib/less/visitor/import-visitor.js +++ b/lib/less/visitor/import-visitor.js @@ -1,4 +1,4 @@ -var contexts = require("../env.js"); +var contexts = require("../contexts.js"); module.exports = function (visitor, tree) { var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { this._visitor = new visitor(this); From fe5e2bf7cb2a4f59f31fa4bd1e21cf099c0a82f7 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 18:33:03 +0100 Subject: [PATCH 052/161] remove dependency injection from functions files apart from when needed for environment --- lib/less/functions/color-blending.js | 126 +++---- lib/less/functions/color.js | 493 +++++++++++++------------- lib/less/functions/data-uri.js | 14 +- lib/less/functions/default.js | 6 +- lib/less/functions/function-caller.js | 9 +- lib/less/functions/index.js | 31 +- lib/less/functions/math.js | 69 ++-- lib/less/functions/number.js | 140 ++++---- lib/less/functions/string.js | 57 +-- lib/less/functions/svg.js | 14 +- lib/less/functions/types.js | 130 +++---- lib/less/non-node-index.js | 2 +- 12 files changed, 559 insertions(+), 532 deletions(-) diff --git a/lib/less/functions/color-blending.js b/lib/less/functions/color-blending.js index c24b549aa..ed2fa071f 100644 --- a/lib/less/functions/color-blending.js +++ b/lib/less/functions/color-blending.js @@ -1,74 +1,74 @@ -module.exports = function(functions, tree) { +var Color = require("../tree/color.js"), + functionRegistry = require("./function-registry.js"); - // Color Blending - // ref: http://www.w3.org/TR/compositing-1 +// Color Blending +// ref: http://www.w3.org/TR/compositing-1 - function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result +function colorBlend(mode, color1, color2) { + var ab = color1.alpha, cb, // backdrop + as = color2.alpha, cs, // source + ar, cr, r = []; // result - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; + ar = as + ab * (1 - as); + for (var i = 0; i < 3; i++) { + cb = color1.rgb[i] / 255; + cs = color2.rgb[i] / 255; + cr = mode(cb, cs); + if (ar) { + cr = (as * cs + ab * (cb - + as * (cb + cs - cr))) / ar; } - - return new(tree.Color)(r, ar); + r[i] = cr * 255; } - var colorBlendModeFunctions = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendModeFunctions.multiply(cb, cs) - : colorBlendModeFunctions.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendModeFunctions.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, + return new(Color)(r, ar); +} - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); +var colorBlendModeFunctions = { + multiply: function(cb, cs) { + return cb * cs; + }, + screen: function(cb, cs) { + return cb + cs - cb * cs; + }, + overlay: function(cb, cs) { + cb *= 2; + return (cb <= 1) + ? colorBlendModeFunctions.multiply(cb, cs) + : colorBlendModeFunctions.screen(cb - 1, cs); + }, + softlight: function(cb, cs) { + var d = 1, e = cb; + if (cs > 0.5) { + e = 1; + d = (cb > 0.25) ? Math.sqrt(cb) + : ((16 * cb - 12) * cb + 4) * cb; } - }; + return cb - (1 - 2 * cs) * e * (d - cb); + }, + hardlight: function(cb, cs) { + return colorBlendModeFunctions.overlay(cs, cb); + }, + difference: function(cb, cs) { + return Math.abs(cb - cs); + }, + exclusion: function(cb, cs) { + return cb + cs - 2 * cb * cs; + }, - for (var f in colorBlendModeFunctions) { - if (colorBlendModeFunctions.hasOwnProperty(f)) { - colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]); - } + // non-w3c functions: + average: function(cb, cs) { + return (cb + cs) / 2; + }, + negation: function(cb, cs) { + return 1 - Math.abs(cb + cs - 1); } - - functions.functionRegistry.addMultiple(colorBlend); }; + +for (var f in colorBlendModeFunctions) { + if (colorBlendModeFunctions.hasOwnProperty(f)) { + colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]); + } +} + +functionRegistry.addMultiple(colorBlend); diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js index 1b493bcea..c08aee8c6 100644 --- a/lib/less/functions/color.js +++ b/lib/less/functions/color.js @@ -1,272 +1,277 @@ -module.exports = function(functions, tree) { - function clamp(val) { - return Math.min(1, Math.max(0, val)); +var Dimension = require("../tree/dimension.js"), + Color = require("../tree/color.js"), + Quoted = require("../tree/quoted.js"), + Anonymous = require("../tree/anonymous.js"), + functionRegistry = require("./function-registry.js"), + colorFunctions; + +function clamp(val) { + return Math.min(1, Math.max(0, val)); +} +function hsla(color) { + return colorFunctions.hsla(color.h, color.s, color.l, color.a); +} +function number(n) { + if (n instanceof Dimension) { + return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); + } else if (typeof(n) === 'number') { + return n; + } else { + throw { + error: "RuntimeError", + message: "color functions take numbers as parameters" + }; } - function hsla(color) { - return colorFunctions.hsla(color.h, color.s, color.l, color.a); +} +function scaled(n, size) { + if (n instanceof Dimension && n.unit.is('%')) { + return parseFloat(n.value * size / 100); + } else { + return number(n); } - function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } - } - function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); +} +colorFunctions = { + rgb: function (r, g, b) { + return colorFunctions.rgba(r, g, b, 1.0); + }, + rgba: function (r, g, b, a) { + var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); + a = number(a); + return new(Color)(rgb, a); + }, + hsl: function (h, s, l) { + return colorFunctions.hsla(h, s, l, 1.0); + }, + hsla: function (h, s, l, a) { + function hue(h) { + 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; } } - } - var colorFunctions = { - rgb: function (r, g, b) { - return colorFunctions.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return colorFunctions.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); + h = (number(h) % 360) / 360; + s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; - return colorFunctions.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, + return colorFunctions.rgba(hue(h + 1/3) * 255, + hue(h) * 255, + hue(h - 1/3) * 255, + a); + }, - hsv: function(h, s, v) { - return colorFunctions.hsva(h, s, v, 1.0); - }, + hsv: function(h, s, v) { + return colorFunctions.hsva(h, s, v, 1.0); + }, - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); + hsva: function(h, s, v, a) { + h = ((number(h) % 360) / 360) * 360; + s = number(s); v = number(v); a = number(a); - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; + var i, f; + i = Math.floor((h / 60) % 6); + f = (h / 60) - i; - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; + var vs = [v, + v * (1 - s), + v * (1 - f * s), + v * (1 - (1 - f) * s)]; + var perm = [[0, 3, 1], + [2, 0, 1], + [1, 0, 3], + [1, 2, 0], + [3, 1, 0], + [0, 1, 2]]; - return colorFunctions.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, + return colorFunctions.rgba(vs[perm[i][0]] * 255, + vs[perm[i][1]] * 255, + vs[perm[i][2]] * 255, + a); + }, - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); + hue: function (color) { + return new(Dimension)(color.toHSL().h); + }, + saturation: function (color) { + return new(Dimension)(color.toHSL().s * 100, '%'); + }, + lightness: function (color) { + return new(Dimension)(color.toHSL().l * 100, '%'); + }, + hsvhue: function(color) { + return new(Dimension)(color.toHSV().h); + }, + hsvsaturation: function (color) { + return new(Dimension)(color.toHSV().s * 100, '%'); + }, + hsvvalue: function (color) { + return new(Dimension)(color.toHSV().v * 100, '%'); + }, + red: function (color) { + return new(Dimension)(color.rgb[0]); + }, + green: function (color) { + return new(Dimension)(color.rgb[1]); + }, + blue: function (color) { + return new(Dimension)(color.rgb[2]); + }, + alpha: function (color) { + return new(Dimension)(color.toHSL().a); + }, + luma: function (color) { + return new(Dimension)(color.luma() * color.alpha * 100, '%'); + }, + luminance: function (color) { + var luminance = + (0.2126 * color.rgb[0] / 255) + + (0.7152 * color.rgb[1] / 255) + + (0.0722 * color.rgb[2] / 255); - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); + return new(Dimension)(luminance * color.alpha * 100, '%'); + }, + saturate: function (color, amount) { + // filter: saturate(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + var hsl = color.toHSL(); - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); + hsl.s += amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + desaturate: function (color, amount) { + var hsl = color.toHSL(); - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); + hsl.s -= amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + lighten: function (color, amount) { + var hsl = color.toHSL(); - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); + hsl.l += amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + darken: function (color, amount) { + var hsl = color.toHSL(); - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); + hsl.l -= amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + fadein: function (color, amount) { + var hsl = color.toHSL(); - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); + hsl.a += amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fadeout: function (color, amount) { + var hsl = color.toHSL(); - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); + hsl.a -= amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fade: function (color, amount) { + var hsl = color.toHSL(); - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; + hsl.a = amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + spin: function (color, amount) { + var hsl = color.toHSL(); + var hue = (hsl.h + amount.value) % 360; - hsl.h = hue < 0 ? 360 + hue : hue; + hsl.h = hue < 0 ? 360 + hue : hue; - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; + return hsla(hsl); + }, + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + mix: function (color1, color2, weight) { + if (!weight) { + weight = new(Dimension)(50); + } + var p = weight.value / 100.0; + var w = p * 2 - 1; + var a = color1.toHSL().a - color2.toHSL().a; - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; + var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; + var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, + color1.rgb[1] * w1 + color2.rgb[1] * w2, + color1.rgb[2] * w1 + color2.rgb[2] * w2]; - var alpha = color1.alpha * p + color2.alpha * (1 - p); + var alpha = color1.alpha * p + color2.alpha * (1 - p); - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return colorFunctions.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = colorFunctions.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = colorFunctions.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - color: function(c) { - if ((c instanceof tree.Quoted) && - (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { - return new(tree.Color)(c.value.slice(1)); - } - if ((c instanceof tree.Color) || (c = tree.Color.fromKeyword(c.value))) { - c.keyword = undefined; - return c; - } - throw { - type: "Argument", - message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" - }; - }, - tint: function(color, amount) { - return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount); + return new(Color)(rgb, alpha); + }, + greyscale: function (color) { + return colorFunctions.desaturate(color, new(Dimension)(100)); + }, + contrast: function (color, dark, light, threshold) { + // filter: contrast(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + if (typeof light === 'undefined') { + light = colorFunctions.rgba(255, 255, 255, 1.0); } - }; - functions.functionRegistry.addMultiple(colorFunctions); + if (typeof dark === 'undefined') { + dark = colorFunctions.rgba(0, 0, 0, 1.0); + } + //Figure out which is actually light and dark! + if (dark.luma() > light.luma()) { + var t = light; + light = dark; + dark = t; + } + if (typeof threshold === 'undefined') { + threshold = 0.43; + } else { + threshold = number(threshold); + } + if (color.luma() < threshold) { + return light; + } else { + return dark; + } + }, + argb: function (color) { + return new(Anonymous)(color.toARGB()); + }, + color: function(c) { + if ((c instanceof Quoted) && + (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { + return new(Color)(c.value.slice(1)); + } + if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) { + c.keyword = undefined; + return c; + } + throw { + type: "Argument", + message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" + }; + }, + tint: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount); + }, + shade: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount); + } }; +functionRegistry.addMultiple(colorFunctions); diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index c60be3aa4..47e1273e5 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -1,8 +1,12 @@ -module.exports = function(functions, tree, less) { - functions.functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { +module.exports = function(less) { + var Anonymous = require("../tree/anonymous.js"), + URL = require("../tree/url.js"), + functionRegistry = require("./function-registry.js"); + + functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { if (!less.environment.supportsDataURI(this.env)) { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); + return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); } var mimetype = mimetypeNode.value; @@ -56,7 +60,7 @@ module.exports = function(functions, tree, less) { console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); } - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); + return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); } } @@ -64,6 +68,6 @@ module.exports = function(functions, tree, less) { : encodeURIComponent(buf); var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); + return new(URL)(new(Anonymous)(uri)); }); }; diff --git a/lib/less/functions/default.js b/lib/less/functions/default.js index ee256c719..8b97e7aa5 100644 --- a/lib/less/functions/default.js +++ b/lib/less/functions/default.js @@ -1,4 +1,5 @@ -var Keyword = require("../tree/keyword.js"); +var Keyword = require("../tree/keyword.js"), + functionRegistry = require("./function-registry.js"); var defaultFunc = { eval: function () { @@ -20,4 +21,7 @@ var defaultFunc = { this.value_ = this.error_ = null; } }; + +functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); + module.exports = defaultFunc; diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js index da86d513d..753c14802 100644 --- a/lib/less/functions/function-caller.js +++ b/lib/less/functions/function-caller.js @@ -1,7 +1,8 @@ -module.exports = function(functions) { +var functionRegistry = require("./function-registry.js"); + var functionCaller = function(name, env, currentFileInfo) { this.name = name.toLowerCase(); - this.function = functions.functionRegistry.get(this.name); + this.function = functionRegistry.get(this.name); this.env = env; this.currentFileInfo = currentFileInfo; }; @@ -11,5 +12,5 @@ functionCaller.prototype.isValid = function() { functionCaller.prototype.call = function(args) { return this.function.apply(this, args); }; -return functionCaller; -}; + +module.exports = functionCaller; diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js index 4600500ae..261b4bc57 100644 --- a/lib/less/functions/index.js +++ b/lib/less/functions/index.js @@ -1,22 +1,19 @@ -module.exports = function(less, tree) { - var functions = {}; - functions.functionRegistry = require("./function-registry.js"); - functions.functionCaller = require("./function-caller.js")(functions); +module.exports = function(less) { + var functions = { + functionRegistry: require("./function-registry.js"), + functionCaller: require("./function-caller.js") + }; //register functions - require("./color.js")(functions, tree); - require("./color-blending.js")(functions, tree); - require("./data-uri.js")(functions, tree, less); - - var defaultFunc = require("./default.js"); - functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); - tree.defaultFunc = defaultFunc; - - require("./math.js")(functions, tree); - require("./number.js")(functions, tree); - require("./string.js")(functions, tree); - require("./svg.js")(functions, tree, less); - require("./types.js")(functions, tree); + require("./default.js"); + require("./color.js"); + require("./color-blending.js"); + require("./data-uri.js")(less); + require("./math.js"); + require("./number.js"); + require("./string.js"); + require("./svg.js")(less); + require("./types.js"); return functions; }; diff --git a/lib/less/functions/math.js b/lib/less/functions/math.js index 4e56d8d2a..947427370 100644 --- a/lib/less/functions/math.js +++ b/lib/less/functions/math.js @@ -1,42 +1,41 @@ -module.exports = function(functions, tree) { +var Dimension = require("../tree/dimension.js"), + functionRegistry = require("./function-registry.js"); - var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" - }; +var mathFunctions = { + // name, unit + ceil: null, + floor: null, + sqrt: null, + abs: null, + tan: "", + sin: "", + cos: "", + atan: "rad", + asin: "rad", + acos: "rad" +}; - function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); +function _math(fn, unit, n) { + if (!(n instanceof Dimension)) { + throw { type: "Argument", message: "argument must be a number" }; } - - for (var f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]); - } + if (unit == null) { + unit = n.unit; + } else { + n = n.unify(); } + return new(Dimension)(fn(parseFloat(n.value)), unit); +} - mathFunctions.round = function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }; - - functions.functionRegistry.addMultiple(mathFunctions); +for (var f in mathFunctions) { + if (mathFunctions.hasOwnProperty(f)) { + mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]); + } +} +mathFunctions.round = function (n, f) { + var fraction = typeof(f) === "undefined" ? 0 : f.value; + return _math(function(num) { return num.toFixed(fraction); }, null, n); }; + +functionRegistry.addMultiple(mathFunctions); diff --git a/lib/less/functions/number.js b/lib/less/functions/number.js index dc6fdfd59..0eb84c9ca 100644 --- a/lib/less/functions/number.js +++ b/lib/less/functions/number.js @@ -1,74 +1,76 @@ -module.exports = function(functions, tree) { - var minMax = function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; +var Dimension = require("../tree/dimension.js"), + Anonymous = require("../tree/anonymous.js"), + functionRegistry = require("./function-registry.js"); + +var minMax = function (isMin, args) { + args = Array.prototype.slice.call(args); + switch(args.length) { + case 0: throw { type: "Argument", message: "one or more arguments required" }; + } + var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, + order = [], // elems only contains original argument values. + values = {}; // key is the unit.toString() for unified Dimension values, + // value is the index into the order array. + for (i = 0; i < args.length; i++) { + current = args[i]; + if (!(current instanceof Dimension)) { + if(Array.isArray(args[i].value)) { + Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); } + continue; } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }; - functions.functionRegistry.addMultiple({ - min: function () { - return minMax(true, arguments); - }, - max: function () { - return minMax(false, arguments); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; + currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(Dimension)(current.value, unitClone).unify() : current.unify(); + unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); + unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; + unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; + j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; + if (j === undefined) { + if(unitStatic !== undefined && unit !== unitStatic) { + throw{ type: "Argument", message: "incompatible types" }; } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); + values[unit] = order.length; + order.push(current); + continue; + } + referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(Dimension)(order[j].value, unitClone).unify() : order[j].unify(); + if ( isMin && currentUnified.value < referenceUnified.value || + !isMin && currentUnified.value > referenceUnified.value) { + order[j] = current; } - }); + } + if (order.length == 1) { + return order[0]; + } + args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); + return new(Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); }; +functionRegistry.addMultiple({ + min: function () { + return minMax(true, arguments); + }, + max: function () { + return minMax(false, arguments); + }, + convert: function (val, unit) { + return val.convertTo(unit.value); + }, + pi: function () { + return new(Dimension)(Math.PI); + }, + mod: function(a, b) { + return new(Dimension)(a.value % b.value, a.unit); + }, + pow: function(x, y) { + if (typeof x === "number" && typeof y === "number") { + x = new(Dimension)(x); + y = new(Dimension)(y); + } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) { + throw { type: "Argument", message: "arguments must be numbers" }; + } + + return new(Dimension)(Math.pow(x.value, y.value), x.unit); + }, + percentage: function (n) { + return new(Dimension)(n.value * 100, '%'); + } +}); diff --git a/lib/less/functions/string.js b/lib/less/functions/string.js index 9851c8918..a9547274e 100644 --- a/lib/less/functions/string.js +++ b/lib/less/functions/string.js @@ -1,30 +1,33 @@ -module.exports = function(functions, tree) { - functions.functionRegistry.addMultiple({ - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; +var Quoted = require("../tree/quoted.js"), + Anonymous = require("../tree/anonymous.js"), + JavaScript = require("../tree/javascript.js"), + functionRegistry = require("./function-registry.js"); - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; +functionRegistry.addMultiple({ + e: function (str) { + return new(Anonymous)(str instanceof JavaScript ? str.evaluated : str.value); + }, + escape: function (str) { + return new(Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); + }, + replace: function (string, pattern, replacement, flags) { + var result = string.value; - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); + result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); + return new(Quoted)(string.quote || '', result, string.escaped); + }, + '%': function (string /* arg, arg, ...*/) { + var args = Array.prototype.slice.call(arguments, 1), + result = string.value; + + for (var i = 0; i < args.length; i++) { + /*jshint loopfunc:true */ + result = result.replace(/%[sda]/i, function(token) { + var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); + return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; + }); } - }); -}; + result = result.replace(/%%/g, '%'); + return new(Quoted)(string.quote || '', result, string.escaped); + } +}); diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js index 0cd71d571..439a2184b 100644 --- a/lib/less/functions/svg.js +++ b/lib/less/functions/svg.js @@ -1,5 +1,11 @@ -module.exports = function(functions, tree, less) { - functions.functionRegistry.add("svg-gradient", function(direction) { +module.exports = function(less) { + var Dimension = require("../tree/dimension.js"), + Color = require("../tree/color.js"), + Anonymous = require("../tree/anonymous.js"), + URL = require("../tree/url.js"), + functionRegistry = require("./function-registry.js"); + + functionRegistry.add("svg-gradient", function(direction) { function throwArgumentDescriptor() { throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; @@ -53,7 +59,7 @@ module.exports = function(functions, tree, less) { position = undefined; } - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { + if (!(color instanceof Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof Dimension))) { throwArgumentDescriptor(); } positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; @@ -72,6 +78,6 @@ module.exports = function(functions, tree, less) { } returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); + return new(URL)(new(Anonymous)(returner)); }); }; diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js index 047343881..dfb51b664 100644 --- a/lib/less/functions/types.js +++ b/lib/less/functions/types.js @@ -1,65 +1,71 @@ -var Keyword = require("../tree/keyword.js"); -module.exports = function(functions, tree) { - var isa = function (n, Type) { - return (n instanceof Type) ? Keyword.True : Keyword.False; - }, - isunit = function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False; - }; - functions.functionRegistry.addMultiple({ - iscolor: function (n) { - return isa(n, tree.Color); - }, - isnumber: function (n) { - return isa(n, tree.Dimension); - }, - isstring: function (n) { - return isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return isa(n, tree.Keyword); - }, - isurl: function (n) { - return isa(n, tree.URL); - }, - ispixel: function (n) { - return isunit(n, 'px'); - }, - ispercentage: function (n) { - return isunit(n, '%'); - }, - isem: function (n) { - return isunit(n, 'em'); - }, - isunit: isunit, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } +var Keyword = require("../tree/keyword.js"), + Dimension = require("../tree/dimension.js"), + Color = require("../tree/color.js"), + Quoted = require("../tree/quoted.js"), + Anonymous = require("../tree/anonymous.js"), + URL = require("../tree/url.js"), + Operation = require("../tree/operation.js"), + functionRegistry = require("./function-registry.js"); + +var isa = function (n, Type) { + return (n instanceof Type) ? Keyword.True : Keyword.False; + }, + isunit = function (n, unit) { + return (n instanceof Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False; + }; +functionRegistry.addMultiple({ + iscolor: function (n) { + return isa(n, Color); + }, + isnumber: function (n) { + return isa(n, Dimension); + }, + isstring: function (n) { + return isa(n, Quoted); + }, + iskeyword: function (n) { + return isa(n, Keyword); + }, + isurl: function (n) { + return isa(n, URL); + }, + ispixel: function (n) { + return isunit(n, 'px'); + }, + ispercentage: function (n) { + return isunit(n, '%'); + }, + isem: function (n) { + return isunit(n, 'em'); + }, + isunit: isunit, + unit: function (val, unit) { + if(!(val instanceof Dimension)) { + throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") }; + } + if (unit) { + if (unit instanceof Keyword) { + unit = unit.value; } else { - unit = ""; + unit = unit.toCSS(); } - return new(tree.Dimension)(val.value, unit); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) ? - values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); + } else { + unit = ""; } - }); -}; + return new(Dimension)(val.value, unit); + }, + "get-unit": function (n) { + return new(Anonymous)(n.unit); + }, + extract: function(values, index) { + index = index.value - 1; // (1-based index) + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + return Array.isArray(values.value) ? + values.value[index] : Array(values)[index]; + }, + length: function(values) { + var n = Array.isArray(values.value) ? values.value.length : 1; + return new Dimension(n); + } +}); diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 2f5bcb020..a14c1c339 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -6,7 +6,7 @@ var less = { less.tree = require('./tree/index.js'); less.visitor = require('./visitor/index.js')(less, less.tree); less.Parser = (require('./parser'))(less, less.tree, less.visitor); -less.functions = (require('./functions/index.js'))(less, less.tree); +less.functions = require('./functions/index.js')(less); less.contexts = require("./contexts.js"); less.tree.sourceMapOutput = require('./source-map-output.js')(less); From e325aec74d396f8bc0a412f80a5107a1f6f4af05 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 18:47:25 +0100 Subject: [PATCH 053/161] Also remove dep-injection from visitors, none of which use environment. make functions used as classes TitleCase --- lib/less/non-node-index.js | 2 +- lib/less/parser.js | 8 +- lib/less/visitor/extend-visitor.js | 733 +++++++++++----------- lib/less/visitor/import-visitor.js | 246 ++++---- lib/less/visitor/index.js | 20 +- lib/less/visitor/join-selector-visitor.js | 78 +-- lib/less/visitor/to-css-visitor.js | 409 ++++++------ lib/less/visitor/visitor.js | 239 ++++--- 8 files changed, 866 insertions(+), 869 deletions(-) diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index a14c1c339..8c1abfc19 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -4,7 +4,7 @@ var less = { }; less.tree = require('./tree/index.js'); -less.visitor = require('./visitor/index.js')(less, less.tree); +less.visitor = require('./visitor/index.js'); less.Parser = (require('./parser'))(less, less.tree, less.visitor); less.functions = require('./functions/index.js')(less); less.contexts = require("./contexts.js"); diff --git a/lib/less/parser.js b/lib/less/parser.js index 7a8744e28..e90995717 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -485,9 +485,9 @@ var Parser = function Parser(env) { try { var preEvalVisitors = [], visitors = [ - new(visitor.joinSelectorVisitor)(), - new(visitor.extendVisitor)(), - new(visitor.toCSSVisitor)({compress: Boolean(options.compress)}) + new(visitor.JoinSelectorVisitor)(), + new(visitor.ExtendVisitor)(), + new(visitor.ToCSSVisitor)({compress: Boolean(options.compress)}) ], i, root = this; if (options.plugins) { @@ -623,7 +623,7 @@ var Parser = function Parser(env) { }; if (env.processImports !== false) { - new visitor.importVisitor(this.imports, finish) + new visitor.ImportVisitor(this.imports, finish) .run(root); } else { return finish(); diff --git a/lib/less/visitor/extend-visitor.js b/lib/less/visitor/extend-visitor.js index bd636143c..9616cd6d8 100644 --- a/lib/less/visitor/extend-visitor.js +++ b/lib/less/visitor/extend-visitor.js @@ -1,417 +1,418 @@ -module.exports = function (visitor, tree) { - /*jshint loopfunc:true */ - - var extendFinderVisitor = function() { - this._visitor = new visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } +var tree = require("../tree/index.js"), + Visitor = require("./visitor.js"); - var i, j, extend, allSelectorsExtendList = [], extendList; +/*jshint loopfunc:true */ - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - rulesetNode.extendOnEveryPath = true; - } - } +var ExtendFinderVisitor = function() { + this._visitor = new Visitor(this); + this.contexts = []; + this.allExtendsStack = [[]]; +}; - // now find every selector and apply the extends that apply to all extends - // and the ones which apply to an individual extend - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; +ExtendFinderVisitor.prototype = { + run: function (root) { + root = this._visitor.visit(root); + root.allExtends = this.allExtendsStack[0]; + return root; + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitRuleset: function (rulesetNode, visitArgs) { + if (rulesetNode.root) { + return; + } - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; + var i, j, extend, allSelectorsExtendList = [], extendList; - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } + // get &:extend(.a); rules which apply to all selectors in this ruleset + var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; + for(i = 0; i < ruleCnt; i++) { + if (rulesetNode.rules[i] instanceof tree.Extend) { + allSelectorsExtendList.push(rules[i]); + rulesetNode.extendOnEveryPath = true; + } + } - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } + // now find every selector and apply the extends that apply to all extends + // and the ones which apply to an individual extend + var paths = rulesetNode.paths; + for(i = 0; i < paths.length; i++) { + var selectorPath = paths[i], + selector = selectorPath[selectorPath.length - 1], + selExtendList = selector.extendList; + + extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) + : allSelectorsExtendList; + + if (extendList) { + extendList = extendList.map(function(allSelectorsExtend) { + return allSelectorsExtend.clone(); + }); } - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; + for(j = 0; j < extendList.length; j++) { + this.foundExtends = true; + extend = extendList[j]; + extend.findSelfSelectors(selectorPath); + extend.ruleset = rulesetNode; + if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } + this.allExtendsStack[this.allExtendsStack.length-1].push(extend); } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; } - }; - - var processExtendsVisitor = function() { - this._visitor = new visitor(this); - }; - - processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } + + this.contexts.push(rulesetNode.selectors); + }, + visitRulesetOut: function (rulesetNode) { + if (!rulesetNode.root) { + this.contexts.length = this.contexts.length - 1; + } + }, + visitMedia: function (mediaNode, visitArgs) { + mediaNode.allExtends = []; + this.allExtendsStack.push(mediaNode.allExtends); + }, + visitMediaOut: function (mediaNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + }, + visitDirective: function (directiveNode, visitArgs) { + directiveNode.allExtends = []; + this.allExtendsStack.push(directiveNode.allExtends); + }, + visitDirectiveOut: function (directiveNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + } +}; + +var ProcessExtendsVisitor = function() { + this._visitor = new Visitor(this); +}; + +ProcessExtendsVisitor.prototype = { + run: function(root) { + var extendFinder = new ExtendFinderVisitor(); + extendFinder.run(root); + if (!extendFinder.foundExtends) { return root; } + root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); + this.allExtendsStack = [root.allExtends]; + return this._visitor.visit(root); + }, + doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { + // + // 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 + + var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; + + iterationCount = iterationCount || 0; + + //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; extendIndex < extendsList.length; extendIndex++){ + for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ + + extend = extendsList[extendIndex]; + targetExtend = extendsListTarget[targetExtendIndex]; + + // look for circular references + if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } + + // find a match in the target extends self selector (the bit before :extend) + selectorPath = [targetExtend.selfSelectors[0]]; + matches = extendVisitor.findMatch(extend, selectorPath); + + if (matches.length) { + + // we found a match, so for each self selector.. + extend.selfSelectors.forEach(function(selfSelector) { + + // process the extend as usual + newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); + + // but now we create a new extend from it + newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); + newExtend.selfSelectors = newSelector; + + // add the extend onto the list of extends for that selector + newSelector[newSelector.length-1].extendList = [newExtend]; + + // record that we need to add it. + extendsToAdd.push(newExtend); + newExtend.ruleset = targetExtend.ruleset; + + //remember its parents for circular references + newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); + } + }); } } + } - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"}; + if (extendsToAdd.length) { + // try to detect circular references to stop a stack overflow. + // may no longer be needed. + this.extendChainCount++; + if (iterationCount > 100) { + var selectorOne = "{unable to calculate}"; + var selectorTwo = "{unable to calculate}"; + try + { + selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); + selectorTwo = extendsToAdd[0].selector.toCSS(); } - - // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; + catch(e) {} + throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"}; } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace + // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e... + return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); + } else { + return extendsToAdd; + } + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitSelector: function (selectorNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitRuleset: function (rulesetNode, visitArgs) { + if (rulesetNode.root) { + return; + } + var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; + // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } + for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { + for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { + selectorPath = rulesetNode.paths[pathIndex]; - matches = this.findMatch(allExtends[extendIndex], selectorPath); + // extending extends happens initially, before the main pass + if (rulesetNode.extendOnEveryPath) { continue; } + var extendList = selectorPath[selectorPath.length-1].extendList; + if (extendList && extendList.length) { continue; } - if (matches.length) { + matches = this.findMatch(allExtends[extendIndex], selectorPath); - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } + if (matches.length) { + + allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { + selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); + }); } } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } + } + rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); + }, + findMatch: function (extend, haystackSelectorPath) { + // + // 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 + // + var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, + targetCombinator, i, + extendVisitor = this, + needleElements = extend.selector.elements, + potentialMatches = [], potentialMatch, matches = []; + + // loop through the haystack elements + for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { + hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; + + for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); + } - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; + for(i = 0; i < potentialMatches.length; i++) { + potentialMatch = potentialMatches[i]; - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } + // 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.value; + if (targetCombinator === '' && hackstackElementIndex === 0) { + targetCombinator = ' '; + } - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } + // if we don't match, null our match to indicate failure + if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || + (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { + potentialMatch = null; + } else { + potentialMatch.matched++; + } - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - potentialMatch = null; - } + // if we are still valid and have finished, test whether we have elements after and whether these are allowed + if (potentialMatch) { + potentialMatch.finished = potentialMatch.matched === needleElements.length; + if (potentialMatch.finished && + (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { + 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; + } + // 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 = needleElements.length; + potentialMatch.endPathIndex = haystackSelectorIndex; + potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match + potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again + matches.push(potentialMatch); } + } else { + potentialMatches.splice(i, 1); + i--; } } } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; + } + return matches; + }, + isElementValuesEqual: function(elementValue1, elementValue2) { + if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { + return elementValue1 === elementValue2; + } + if (elementValue1 instanceof tree.Attribute) { + if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) { + return false; } - if (elementValue1 instanceof tree.Attribute) { - if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) { + if (!elementValue1.value || !elementValue2.value) { + if (elementValue1.value || elementValue2.value) { return false; } - if (!elementValue1.value || !elementValue2.value) { - if (elementValue1.value || elementValue2.value) { + return true; + } + elementValue1 = elementValue1.value.value || elementValue1.value; + elementValue2 = elementValue2.value.value || elementValue2.value; + return elementValue1 === elementValue2; + } + elementValue1 = elementValue1.value; + elementValue2 = elementValue2.value; + if (elementValue1 instanceof tree.Selector) { + if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { + return false; + } + for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } + return true; + } + return false; + }, + extendSelector:function (matches, selectorPath, replacementSelector) { + + //for a set of matches, replace each match with the replacement selector + + var currentSelectorPathIndex = 0, + currentSelectorPathElementIndex = 0, + path = [], + matchIndex, + selector, + firstElement, + match, + newElements; + + for (matchIndex = 0; matchIndex < matches.length; matchIndex++) { + match = matches[matchIndex]; + selector = selectorPath[match.pathIndex]; + firstElement = new tree.Element( + match.initialCombinator, + replacementSelector.elements[0].value, + replacementSelector.elements[0].index, + replacementSelector.elements[0].currentFileInfo + ); + + if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { + path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathElementIndex = 0; + currentSelectorPathIndex++; + } - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); + newElements = selector.elements + .slice(currentSelectorPathElementIndex, match.index) + .concat([firstElement]) + .concat(replacementSelector.elements.slice(1)); - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); + if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { + path[path.length - 1].elements = + path[path.length - 1].elements.concat(newElements); + } else { + path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } + path.push(new tree.Selector( + newElements + )); } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathIndex = match.endPathIndex; + currentSelectorPathElementIndex = match.endPathElementIndex; + if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { + currentSelectorPathElementIndex = 0; currentSelectorPathIndex++; } + } - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; + if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { + path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathIndex++; } - }; - return processExtendsVisitor; + path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); + + return path; + }, + visitRulesetOut: function (rulesetNode) { + }, + visitMedia: function (mediaNode, visitArgs) { + var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); + newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); + this.allExtendsStack.push(newAllExtends); + }, + visitMediaOut: function (mediaNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + }, + visitDirective: function (directiveNode, visitArgs) { + var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); + newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); + this.allExtendsStack.push(newAllExtends); + }, + visitDirectiveOut: function (directiveNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + } }; + +module.exports = ProcessExtendsVisitor; diff --git a/lib/less/visitor/import-visitor.js b/lib/less/visitor/import-visitor.js index efff072a3..f7068df44 100644 --- a/lib/less/visitor/import-visitor.js +++ b/lib/less/visitor/import-visitor.js @@ -1,145 +1,145 @@ -var contexts = require("../contexts.js"); -module.exports = function (visitor, tree) { - var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new contexts.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } +var contexts = require("../contexts.js"), + Visitor = require("./visitor.js"); + +var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { + this._visitor = new Visitor(this); + this._importer = importer; + this._finish = finish; + this.env = evalEnv || new contexts.evalEnv(); + this.importCount = 0; + this.onceFileDetectionMap = onceFileDetectionMap || {}; + this.recursionDetector = {}; + if (recursionDetector) { + for(var fullFilename in recursionDetector) { + if (recursionDetector.hasOwnProperty(fullFilename)) { + this.recursionDetector[fullFilename] = true; } } - }; + } +}; - ImportVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } +ImportVisitor.prototype = { + isReplacing: true, + run: function (root) { + var error; + try { + // process the contents + this._visitor.visit(root); + } + catch(e) { + error = e; + } - this.isFinished = true; + this.isFinished = true; + + if (this.importCount === 0) { + this._finish(error); + } + }, + visitImport: function (importNode, visitArgs) { + var importVisitor = this, + evaldImportNode, + inlineCSS = importNode.options.inline; - if (this.importCount === 0) { - this._finish(error); + if (!importNode.css || inlineCSS) { + + try { + evaldImportNode = importNode.evalForImport(this.env); + } catch(e){ + if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } + // attempt to eval properly and treat as css + importNode.css = true; + // if that fails, this error will be thrown + importNode.error = e; } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - if (!importNode.css || inlineCSS) { + if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { + importNode = evaldImportNode; + this.importCount++; + var env = new contexts.evalEnv(this.env, this.env.frames.slice(0)); - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; + if (importNode.options.multiple) { + env.importMultiple = true; } - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new contexts.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; + this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { + if (e && !e.filename) { + e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { - e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; - } - - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - if (!env.importMultiple) { - if (duplicateImport) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } + var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; + if (!env.importMultiple) { + if (duplicateImport) { + importNode.skip = true; + } else { + importNode.skip = function() { + if (fullPath in importVisitor.onceFileDetectionMap) { + return true; + } + importVisitor.onceFileDetectionMap[fullPath] = true; + return false; + }; } + } - var subFinish = function(e) { - importVisitor.importCount--; + var subFinish = function(e) { + importVisitor.importCount--; - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; + if (importVisitor.importCount === 0 && importVisitor.isFinished) { + importVisitor._finish(e); + } + }; - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; + if (root) { + importNode.root = root; + importNode.importedFilename = fullPath; - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(ImportVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } + if (!inlineCSS && (env.importMultiple || !duplicateImport)) { + importVisitor.recursionDetector[fullPath] = true; + new(ImportVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) + .run(root); + return; } + } - subFinish(); - }); - } + subFinish(); + }); } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.rules[0]); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); } - }; - return ImportVisitor; + visitArgs.visitDeeper = false; + return importNode; + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + return ruleNode; + }, + visitDirective: function (directiveNode, visitArgs) { + this.env.frames.unshift(directiveNode); + return directiveNode; + }, + visitDirectiveOut: function (directiveNode) { + this.env.frames.shift(); + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + this.env.frames.unshift(mixinDefinitionNode); + return mixinDefinitionNode; + }, + visitMixinDefinitionOut: function (mixinDefinitionNode) { + this.env.frames.shift(); + }, + visitRuleset: function (rulesetNode, visitArgs) { + this.env.frames.unshift(rulesetNode); + return rulesetNode; + }, + visitRulesetOut: function (rulesetNode) { + this.env.frames.shift(); + }, + visitMedia: function (mediaNode, visitArgs) { + this.env.frames.unshift(mediaNode.rules[0]); + return mediaNode; + }, + visitMediaOut: function (mediaNode) { + this.env.frames.shift(); + } }; +module.exports = ImportVisitor; diff --git a/lib/less/visitor/index.js b/lib/less/visitor/index.js index 243910e6b..8a09617f0 100644 --- a/lib/less/visitor/index.js +++ b/lib/less/visitor/index.js @@ -1,13 +1,9 @@ -module.exports = function(less, tree) { - var visitor = require("./visitor")(tree), - visitors = { - visitor: visitor - }; - - visitors.importVisitor = require('./import-visitor.js')(visitor, tree); - visitors.extendVisitor = require('./extend-visitor.js')(visitor, tree); - visitors.joinSelectorVisitor = require('./join-selector-visitor.js')(visitor); - visitors.toCSSVisitor = require('./to-css-visitor.js')(visitor, tree); - return visitors; +var visitors = { + Visitor: require("./visitor"), + ImportVisitor: require('./import-visitor.js'), + ExtendVisitor: require('./extend-visitor.js'), + JoinSelectorVisitor: require('./join-selector-visitor.js'), + ToCSSVisitor: require('./to-css-visitor.js') +}; -}; \ No newline at end of file +module.exports = visitors; diff --git a/lib/less/visitor/join-selector-visitor.js b/lib/less/visitor/join-selector-visitor.js index bc511d98e..05f8732bf 100644 --- a/lib/less/visitor/join-selector-visitor.js +++ b/lib/less/visitor/join-selector-visitor.js @@ -1,45 +1,45 @@ -module.exports = function (visitor) { - var joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new visitor(this); - }; +var Visitor = require("./visitor.js"); - joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, +var JoinSelectorVisitor = function() { + this.contexts = [[]]; + this._visitor = new Visitor(this); +}; - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; +JoinSelectorVisitor.prototype = { + run: function (root) { + return this._visitor.visit(root); + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, - this.contexts.push(paths); + visitRuleset: function (rulesetNode, visitArgs) { + var context = this.contexts[this.contexts.length - 1], + paths = [], selectors; - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; + this.contexts.push(paths); + + if (! rulesetNode.root) { + selectors = rulesetNode.selectors; + if (selectors) { + selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); + rulesetNode.selectors = selectors.length ? selectors : (selectors = null); + if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); + if (!selectors) { rulesetNode.rules = null; } + rulesetNode.paths = paths; } - }; - - return joinSelectorVisitor; -}; \ No newline at end of file + }, + visitRulesetOut: function (rulesetNode) { + this.contexts.length = this.contexts.length - 1; + }, + visitMedia: function (mediaNode, visitArgs) { + var context = this.contexts[this.contexts.length - 1]; + mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); + } +}; + +module.exports = JoinSelectorVisitor; diff --git a/lib/less/visitor/to-css-visitor.js b/lib/less/visitor/to-css-visitor.js index cc3a9008d..1a8f5e248 100644 --- a/lib/less/visitor/to-css-visitor.js +++ b/lib/less/visitor/to-css-visitor.js @@ -1,244 +1,245 @@ -module.exports = function (visitor, tree) { - var toCSSVisitor = function(env) { - this._visitor = new visitor(this); - this._env = env; - }; - - toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, +var tree = require("../tree/index.js"), + Visitor = require("./visitor.js"); - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, +var ToCSSVisitor = function(env) { + this._visitor = new Visitor(this); + this._env = env; +}; + +ToCSSVisitor.prototype = { + isReplacing: true, + run: function (root) { + return this._visitor.visit(root); + }, - visitExtend: function (extendNode, visitArgs) { + visitRule: function (ruleNode, visitArgs) { + if (ruleNode.variable) { return []; - }, + } + return ruleNode; + }, + + visitMixinDefinition: function (mixinNode, visitArgs) { + // 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 = []; + return []; + }, + + visitExtend: function (extendNode, visitArgs) { + return []; + }, + + visitComment: function (commentNode, visitArgs) { + if (commentNode.isSilent(this._env)) { + return []; + } + return commentNode; + }, - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, + visitMedia: function(mediaNode, visitArgs) { + mediaNode.accept(this._visitor); + visitArgs.visitDeeper = false; - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; + if (!mediaNode.rules.length) { + return []; + } + return mediaNode; + }, - if (!mediaNode.rules.length) { + visitDirective: function(directiveNode, visitArgs) { + if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { + return []; + } + 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 (this.charset) { + if (directiveNode.debugInfo) { + var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); + comment.debugInfo = directiveNode.debugInfo; + return this._visitor.visit(comment); + } return []; } - return mediaNode; - }, + this.charset = true; + } + if (directiveNode.rules && directiveNode.rules.rules) { + this._mergeRules(directiveNode.rules.rules); + } + return directiveNode; + }, + + checkPropertiesInRoot: function(rules) { + var ruleNode; + for(var i = 0; i < rules.length; i++) { + ruleNode = rules[i]; + if (ruleNode instanceof tree.Rule && !ruleNode.variable) { + throw { message: "properties must be inside selector blocks, they cannot be in the root.", + index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; + } + } + }, - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; + visitRuleset: function (rulesetNode, visitArgs) { + var rule, rulesets = []; + if (rulesetNode.firstRoot) { + this.checkPropertiesInRoot(rulesetNode.rules); + } + if (! rulesetNode.root) { + if (rulesetNode.paths) { + rulesetNode.paths = rulesetNode.paths + .filter(function(p) { + var i; + if (p[0].elements[0].combinator.value === ' ') { + p[0].elements[0].combinator = new(tree.Combinator)(''); + } + for(i = 0; i < p.length; i++) { + if (p[i].getIsReferenced() && p[i].getIsOutput()) { + return true; + } + } + return false; + }); } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; + + // Compile rules and rulesets + var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; + for (var i = 0; i < nodeRuleCnt; ) { + rule = nodeRules[i]; + if (rule && rule.rules) { + // visit because we are moving them out from being a child + rulesets.push(this._visitor.visit(rule)); + nodeRules.splice(i, 1); + nodeRuleCnt--; + continue; } - this.charset = true; - } - if (directiveNode.rules && directiveNode.rules.rules) { - this._mergeRules(directiveNode.rules.rules); + i++; } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } + // 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._visitor); + } else { + rulesetNode.rules = null; } - }, + visitArgs.visitDeeper = false; - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); + nodeRules = rulesetNode.rules; + if (nodeRules) { + this._mergeRules(nodeRules); + nodeRules = rulesetNode.rules; } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - + if (nodeRules) { + this._removeDuplicateRules(nodeRules); nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } + } - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } + // now decide whether we keep the ruleset + if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { + rulesets.splice(0, 0, rulesetNode); } - if (rulesets.length === 1) { - return rulesets[0]; + } else { + rulesetNode.accept(this._visitor); + visitArgs.visitDeeper = false; + if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { + rulesets.splice(0, 0, rulesetNode); } - return rulesets; - }, + } + if (rulesets.length === 1) { + return rulesets[0]; + } + return rulesets; + }, - _removeDuplicateRules: function(rules) { - if (!rules) { return; } + _removeDuplicateRules: function(rules) { + if (!rules) { return; } - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; + // remove duplicates + var ruleCache = {}, + ruleList, rule, i; - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; + for(i = rules.length - 1; i >= 0 ; i--) { + rule = rules[i]; + if (rule instanceof tree.Rule) { + if (!ruleCache[rule.name]) { + ruleCache[rule.name] = rule; + } else { + ruleList = ruleCache[rule.name]; + if (ruleList instanceof tree.Rule) { + ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; + } + var ruleCSS = rule.toCSS(this._env); + if (ruleList.indexOf(ruleCSS) !== -1) { + rules.splice(i, 1); } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } + ruleList.push(ruleCSS); } } } - }, + } + }, - _mergeRules: function (rules) { - if (!rules) { return; } + _mergeRules: function (rules) { + if (!rules) { return; } - var groups = {}, - parts, - rule, - key; + var groups = {}, + parts, + rule, + key; - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; + for (var i = 0; i < rules.length; i++) { + rule = rules[i]; - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); + if ((rule instanceof tree.Rule) && rule.merge) { + key = [rule.name, + rule.important ? "!" : ""].join(","); - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); + if (!groups[key]) { + groups[key] = []; + } else { + rules.splice(i--, 1); } + + groups[key].push(rule); } + } - Object.keys(groups).map(function (k) { + Object.keys(groups).map(function (k) { - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } + function toExpression(values) { + return new (tree.Expression)(values.map(function (p) { + return p.value; + })); + } - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } + function toValue(values) { + return new (tree.Value)(values.map(function (p) { + return p; + })); + } - parts = groups[k]; + parts = groups[k]; - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; + if (parts.length > 1) { + rule = parts[0]; + var spacedGroups = []; + var lastSpacedGroup = []; + parts.map(function (p) { + if (p.merge==="+") { + if (lastSpacedGroup.length > 0) { + spacedGroups.push(toExpression(lastSpacedGroup)); } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - - return toCSSVisitor; + lastSpacedGroup = []; + } + lastSpacedGroup.push(p); + }); + spacedGroups.push(toExpression(lastSpacedGroup)); + rule.value = toValue(spacedGroups); + } + }); + } }; + +module.exports = ToCSSVisitor; diff --git a/lib/less/visitor/visitor.js b/lib/less/visitor/visitor.js index d6cade478..cd0b32868 100644 --- a/lib/less/visitor/visitor.js +++ b/lib/less/visitor/visitor.js @@ -1,146 +1,145 @@ -module.exports = function (tree) { +var tree = require("../tree/index.js"); + +var _visitArgs = { visitDeeper: true }, + _hasIndexed = false; + +function _noop(node) { + return node; +} + +function indexNodeTypes(parent, ticker) { + // add .typeIndex to tree node types for lookup table + var key, child; + for (key in parent) { + if (parent.hasOwnProperty(key)) { + child = parent[key]; + switch (typeof child) { + case "function": + // ignore bound functions directly on tree which do not have a prototype + // or aren't nodes + if (child.prototype && child.prototype.type) { + child.prototype.typeIndex = ticker++; + } + break; + case "object": + ticker = indexNodeTypes(child, ticker); + break; + } + } + } + return ticker; +} - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; +var Visitor = function(implementation) { + this._implementation = implementation; + this._visitFnCache = []; - function _noop(node) { - return node; + if (!_hasIndexed) { + indexNodeTypes(tree, 1); + _hasIndexed = true; } +}; - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } +Visitor.prototype = { + visit: function(node) { + if (!node) { + return node; } - return ticker; - } - var visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; + var nodeTypeIndex = node.typeIndex; + if (!nodeTypeIndex) { + return node; + } - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; + var visitFnCache = this._visitFnCache, + impl = this._implementation, + aryIndx = nodeTypeIndex << 1, + outAryIndex = aryIndx | 1, + func = visitFnCache[aryIndx], + funcOut = visitFnCache[outAryIndex], + visitArgs = _visitArgs, + fnName; + + visitArgs.visitDeeper = true; + + if (!func) { + fnName = "visit" + node.type; + func = impl[fnName] || _noop; + funcOut = impl[fnName + "Out"] || _noop; + visitFnCache[aryIndx] = func; + visitFnCache[outAryIndex] = funcOut; } - }; - visitor.prototype = { - visit: function(node) { - if (!node) { - return node; + if (func !== _noop) { + var newNode = func.call(impl, node, visitArgs); + if (impl.isReplacing) { + node = newNode; } + } - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } + if (visitArgs.visitDeeper && node && node.accept) { + node.accept(this); + } - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } + if (funcOut != _noop) { + funcOut.call(impl, node); + } - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } + return node; + }, + visitArray: function(nodes, nonReplacing) { + if (!nodes) { + return nodes; + } - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } + var cnt = nodes.length, i; - if (funcOut != _noop) { - funcOut.call(impl, node); + // Non-replacing + if (nonReplacing || !this._implementation.isReplacing) { + for (i = 0; i < cnt; i++) { + this.visit(nodes[i]); } + return nodes; + } - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; + // Replacing + var out = []; + for (i = 0; i < cnt; i++) { + var evald = this.visit(nodes[i]); + if (!evald.splice) { + out.push(evald); + } else if (evald.length) { + this.flatten(evald, out); } + } + return out; + }, + flatten: function(arr, out) { + if (!out) { + out = []; + } - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } + var cnt, i, item, + nestedCnt, j, nestedItem; - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; + for (i = 0, cnt = arr.length; i < cnt; i++) { + item = arr[i]; + if (!item.splice) { + out.push(item); + continue; } - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } + for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { + nestedItem = item[j]; + if (!nestedItem.splice) { + out.push(nestedItem); + } else if (nestedItem.length) { + this.flatten(nestedItem, out); } } - - return out; } - }; - return visitor; + + return out; + } }; +module.exports = Visitor; From 5148db6e3d1112b628e4f63f9a655d616df3d28a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 18:50:43 +0100 Subject: [PATCH 054/161] move source map output into root directory --- lib/less/non-node-index.js | 2 +- lib/less/parser.js | 2 +- lib/less/source-map-output.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 8c1abfc19..d2b67ba0d 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -9,6 +9,6 @@ less.Parser = (require('./parser'))(less, less.tree, less.visitor); less.functions = require('./functions/index.js')(less); less.contexts = require("./contexts.js"); -less.tree.sourceMapOutput = require('./source-map-output.js')(less); +less.SourceMapOutput = require('./source-map-output.js')(less); module.exports = less; diff --git a/lib/less/parser.js b/lib/less/parser.js index e90995717..4f0d9157c 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -515,7 +515,7 @@ var Parser = function Parser(env) { } if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( + evaldRoot = new less.SourceMapOutput( { contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, writeSourceMap: options.writeSourceMap, diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 5d7d6a877..1bb1fac2d 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -1,6 +1,6 @@ module.exports = function (less) { - var sourceMapOutput = function (options) { + var SourceMapOutput = function (options) { this._css = []; this._rootNode = options.rootNode; this._writeSourceMap = options.writeSourceMap; @@ -24,7 +24,7 @@ module.exports = function (less) { this._column = 0; }; - sourceMapOutput.prototype.normalizeFilename = function(filename) { + SourceMapOutput.prototype.normalizeFilename = function(filename) { filename = filename.replace(/\\/g, '/'); if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { @@ -36,7 +36,7 @@ module.exports = function (less) { return (this._sourceMapRootpath || "") + filename; }; - sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { + SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { //ignore adding empty strings if (!chunk) { @@ -92,11 +92,11 @@ module.exports = function (less) { this._css.push(chunk); }; - sourceMapOutput.prototype.isEmpty = function() { + SourceMapOutput.prototype.isEmpty = function() { return this._css.length === 0; }; - sourceMapOutput.prototype.toCSS = function(env) { + SourceMapOutput.prototype.toCSS = function(env) { this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); if (this._outputSourceFiles) { @@ -138,5 +138,5 @@ module.exports = function (less) { return this._css.join(''); }; - return sourceMapOutput; + return SourceMapOutput; }; From f02d920b12b2f3cbdb4dbd715c93e600c0fe36b7 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 24 Aug 2014 18:56:01 +0100 Subject: [PATCH 055/161] move parser files into a parser directory --- lib/less/non-node-index.js | 2 +- lib/less/{ => parser}/chunker.js | 0 lib/less/{ => parser}/parser.js | 10 ++++++---- 3 files changed, 7 insertions(+), 5 deletions(-) rename lib/less/{ => parser}/chunker.js (100%) rename lib/less/{ => parser}/parser.js (99%) diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index d2b67ba0d..9279f8d14 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -5,7 +5,7 @@ var less = { less.tree = require('./tree/index.js'); less.visitor = require('./visitor/index.js'); -less.Parser = (require('./parser'))(less, less.tree, less.visitor); +less.Parser = (require('./parser/parser.js'))(less); less.functions = require('./functions/index.js')(less); less.contexts = require("./contexts.js"); diff --git a/lib/less/chunker.js b/lib/less/parser/chunker.js similarity index 100% rename from lib/less/chunker.js rename to lib/less/parser/chunker.js diff --git a/lib/less/parser.js b/lib/less/parser/parser.js similarity index 99% rename from lib/less/parser.js rename to lib/less/parser/parser.js index 4f0d9157c..7e1ecb5d1 100644 --- a/lib/less/parser.js +++ b/lib/less/parser/parser.js @@ -1,8 +1,10 @@ -var chunker = require('./chunker.js'); -var LessError = require('./less-error.js'); -var contexts = require("./contexts.js"); +var chunker = require('./chunker.js'), + LessError = require('../less-error.js'), + tree = require("../tree/index.js"), + visitor = require("../visitor/index.js"), + contexts = require("../contexts.js"); -module.exports = function(less, tree, visitor) { +module.exports = function(less) { // // less.js - parser // From 87203788a7ed0bfc9459bb14e86133484e9bf208 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 25 Aug 2014 09:28:17 +0100 Subject: [PATCH 056/161] seperate out imports and tidy a few un-necessary optimisations --- lib/less/parser/imports.js | 78 +++++++++++++++++++++++++++++++++ lib/less/parser/parser.js | 90 +++----------------------------------- 2 files changed, 85 insertions(+), 83 deletions(-) create mode 100644 lib/less/parser/imports.js diff --git a/lib/less/parser/imports.js b/lib/less/parser/imports.js new file mode 100644 index 000000000..bec1ce9e6 --- /dev/null +++ b/lib/less/parser/imports.js @@ -0,0 +1,78 @@ +var contexts = require("../contexts.js"); +module.exports = function(less, env, Parser) { + var rootFilename = env && env.filename; + return { + paths: env.paths || [], // Search paths, when importing + queue: [], // Files which haven't been imported yet + files: env.files, // Holds the imported parse trees + contents: env.contents, // Holds the imported file contents + contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less + mime: env.mime, // MIME type of .less files + error: null, // Error in parsing/evaluating an import + push: function (path, currentFileInfo, importOptions, callback) { + var parserImports = this; + this.queue.push(path); + + var fileParsedFunc = function (e, root, fullPath) { + parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue + + var importedPreviously = fullPath === rootFilename; + + parserImports.files[fullPath] = root; // Store the root + + if (e && !parserImports.error) { parserImports.error = e; } + + callback(e, root, importedPreviously, fullPath); + }; + + var newFileInfo = { + relativeUrls: env.relativeUrls, + entryPath: currentFileInfo.entryPath, + rootpath: currentFileInfo.rootpath, + rootFilename: currentFileInfo.rootFilename + }; + + less.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { + if (e) { + fileParsedFunc(e); + return; + } + + // Pass on an updated rootpath if path of imported file is relative and file + // is in a (sub|sup) directory + // + // Examples: + // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', + // then rootpath should become 'less/module/nav/' + // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // then rootpath should become 'less/../' + newFileInfo.currentDirectory = less.environment.getPath(env, resolvedFilename); + if(newFileInfo.relativeUrls) { + newFileInfo.rootpath = less.environment.join((env.rootpath || ""), less.environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!less.environment.isPathAbsolute(env, newFileInfo.rootpath) && less.environment.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = less.environment.join(newFileInfo.entryPath, newFileInfo.rootpath); + } + } + newFileInfo.filename = resolvedFilename; + + var newEnv = new contexts.parseEnv(env); + + newEnv.currentFileInfo = newFileInfo; + newEnv.processImports = false; + newEnv.contents[resolvedFilename] = contents; + + if (currentFileInfo.reference || importOptions.reference) { + newFileInfo.reference = true; + } + + if (importOptions.inline) { + fileParsedFunc(null, contents, resolvedFilename); + } else { + new(Parser)(newEnv).parse(contents, function (e, root) { + fileParsedFunc(e, root, resolvedFilename); + }); + } + }); + } + }; +}; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 0ce0b4d7d..b26cf6b86 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -2,7 +2,8 @@ var chunker = require('./chunker.js'), LessError = require('../less-error.js'), tree = require("../tree/index.js"), visitor = require("../visitor/index.js"), - contexts = require("../contexts.js"); + contexts = require("../contexts.js"), + getImportManager = require("./imports.js"); module.exports = function(less) { // @@ -49,89 +50,16 @@ var Parser = function Parser(env) { current, // current chunk currentPos, // index of current chunk, in `input` parser, - parsers, - rootFilename = env && env.filename; + parsers; // Top parser on an import tree must be sure there is one "env" // which will then be passed around by reference. if (!(env instanceof contexts.parseEnv)) { env = new contexts.parseEnv(env); } + this.env = env; - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - var newFileInfo = { - relativeUrls: env.relativeUrls, - entryPath: currentFileInfo.entryPath, - rootpath: currentFileInfo.rootpath, - rootFilename: currentFileInfo.rootFilename - }; - - less.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { - if (e) { - fileParsedFunc(e); - return; - } - - // Pass on an updated rootpath if path of imported file is relative and file - // is in a (sub|sup) directory - // - // Examples: - // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', - // then rootpath should become 'less/module/nav/' - // - If path of imported file is '../mixins.less' and rootpath is 'less/', - // then rootpath should become 'less/../' - newFileInfo.currentDirectory = less.environment.getPath(env, resolvedFilename); - if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = less.environment.join((env.rootpath || ""), less.environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); - if (!less.environment.isPathAbsolute(env, newFileInfo.rootpath) && less.environment.alwaysMakePathsAbsolute()) { - newFileInfo.rootpath = less.environment.join(newFileInfo.entryPath, newFileInfo.rootpath); - } - } - newFileInfo.filename = resolvedFilename; - - var newEnv = new contexts.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[resolvedFilename] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, resolvedFilename); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, resolvedFilename); - }); - } - }); - } - }; + var imports = this.imports = getImportManager(less, env, Parser); function save() { currentPos = i; @@ -220,8 +148,6 @@ var Parser = function Parser(env) { return m.length === 1 ? m[0] : m; } - var _$re = $re; - // Specialization of $(tok) function $char(tok) { if (input.charAt(i) !== tok) { @@ -376,8 +302,6 @@ var Parser = function Parser(env) { }; } - this.env = env = env || {}; - // // The Parser // @@ -678,7 +602,7 @@ var Parser = function Parser(env) { // block rule: at the root level. // primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; + var mixin = this.mixin, root = [], node; while (current) { @@ -1384,7 +1308,7 @@ var Parser = function Parser(env) { // Selectors are made out of one or more Elements, see above. // selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; + var index = i, elements, extendList, c, e, extend, when, condition; while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { if (when) { From f3c323810c8ea9688eb3c86dd4021ebba401ce2c Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 25 Aug 2014 09:32:41 +0100 Subject: [PATCH 057/161] make some corrections to comment --- lib/less/parser/parser.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index b26cf6b86..bc5052b1b 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -332,8 +332,9 @@ var Parser = function Parser(env) { input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; parser.imports.contents[env.currentFileInfo.filename] = str; - // chunking makes things quicker - // but it is a non-perfect parse - it can't recognise + // chunking apparantly makes things quicker (but my tests indicate + // it might actually make things slower in node at least) + // and it is a non-perfect parse - it can't recognise // unquoted urls, meaning it can't distinguish comments // meaning comments with quotes or {}() in them get 'counted' // and then lead to parse errors. From 41732cc60bbcf5191ebb5bf1794719963f84671d Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 25 Aug 2014 12:19:04 +0100 Subject: [PATCH 058/161] pull out the input processing out of the parser class and a few small improvements - the no js option now applies to all js, including that within quotes. The Javascript node now also returns the right index. --- lib/less/contexts.js | 4 +- lib/less/parser/parser-input.js | 285 +++++++ lib/less/parser/parser.js | 749 ++++++------------ lib/less/tree/js-eval-node.js | 5 + test/less/errors/javascript-error.txt | 2 +- test/less/errors/javascript-undefined-var.txt | 2 +- 6 files changed, 548 insertions(+), 499 deletions(-) create mode 100644 lib/less/parser/parser-input.js diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 5df2cf803..269048aec 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -24,7 +24,6 @@ var parseCopyProperties = [ 'compress', // option - whether to compress 'processImports', // option - whether to process imports. if false then imports will not be imported 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. 'mime', // browser only - mime type for sheet import 'useFileCache', // browser only - whether to use the per file session cache @@ -77,7 +76,8 @@ var evalCopyProperties = [ 'cleancss', // whether to compress with clean-css 'sourceMap', // whether to output a source map 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens + 'urlArgs', // whether to add args into url tokens + 'javascriptEnabled'// option - whether JavaScript is enabled. if undefined, defaults to true ]; contexts.evalEnv = function(options, frames) { diff --git a/lib/less/parser/parser-input.js b/lib/less/parser/parser-input.js new file mode 100644 index 000000000..7b03e249b --- /dev/null +++ b/lib/less/parser/parser-input.js @@ -0,0 +1,285 @@ +var chunker = require('./chunker.js'), + LessError = require('../less-error.js'); +module.exports = function() { + var input, // LeSS input string + j, // current chunk + saveStack = [], // holds state for backtracking + furthest, // furthest index the parser has gone to + furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause + chunks, // chunkified input + current, // current chunk + currentPos, // index of current chunk, in `input` + parserInput = {}; + + parserInput.save = function() { + currentPos = parserInput.i; + saveStack.push( { current: current, i: parserInput.i, j: j }); + }; + parserInput.restore = function(possibleErrorMessage) { + if (parserInput.i > furthest) { + furthest = parserInput.i; + furthestPossibleErrorMessage = possibleErrorMessage; + } + var state = saveStack.pop(); + current = state.current; + currentPos = parserInput.i = state.i; + j = state.j; + }; + parserInput.forget = function() { + saveStack.pop(); + }; + function sync() { + if (parserInput.i > currentPos) { + current = current.slice(parserInput.i - currentPos); + currentPos = parserInput.i; + } + } + parserInput.isWhitespace = function (offset) { + var pos = parserInput.i + (offset || 0), + code = input.charCodeAt(pos); + return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF); + }; + // + // Parse from a token, regexp or string, and move forward if match + // + parserInput.$ = function(tok) { + var tokType = typeof tok, + match, length; + + // Either match a single character in the input, + // or match a regexp in the current chunk (`current`). + // + if (tokType === "string") { + if (input.charAt(parserInput.i) !== tok) { + return null; + } + skipWhitespace(1); + return tok; + } + + // regexp + sync(); + if (! (match = tok.exec(current))) { + return null; + } + + length = match[0].length; + + // The match is confirmed, add the match length to `i`, + // 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. + // + skipWhitespace(length); + + if(typeof(match) === 'string') { + return match; + } else { + return match.length === 1 ? match[0] : match; + } + }; + + // Specialization of $(tok) + parserInput.$re = function(tok) { + if (parserInput.i > currentPos) { + current = current.slice(parserInput.i - currentPos); + currentPos = parserInput.i; + } + var m = tok.exec(current); + if (!m) { + return null; + } + + skipWhitespace(m[0].length); + if(typeof m === "string") { + return m; + } + + return m.length === 1 ? m[0] : m; + }; + + // Specialization of $(tok) + parserInput.$char = function(tok) { + if (input.charAt(parserInput.i) !== tok) { + return null; + } + skipWhitespace(1); + return tok; + }; + + var CHARCODE_SPACE = 32, + CHARCODE_TAB = 9, + CHARCODE_LF = 10, + CHARCODE_CR = 13, + CHARCODE_PLUS = 43, + CHARCODE_COMMA = 44, + CHARCODE_FORWARD_SLASH = 47, + CHARCODE_9 = 57; + + parserInput.autoCommentAbsorb = true; + parserInput.commentStore = []; + parserInput.finished = false; + + var skipWhitespace = function(length) { + var oldi = parserInput.i, oldj = j, + curr = parserInput.i - currentPos, + endIndex = parserInput.i + current.length - curr, + mem = (parserInput.i += length), + inp = input, + c, nextChar, comment; + + for (; parserInput.i < endIndex; parserInput.i++) { + c = inp.charCodeAt(parserInput.i); + + if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) { + nextChar = inp[parserInput.i + 1]; + if (nextChar === '/') { + comment = {index: parserInput.i, isLineComment: true}; + var nextNewLine = inp.indexOf("\n", parserInput.i + 1); + if (nextNewLine < 0) { + nextNewLine = endIndex; + } + parserInput.i = nextNewLine; + comment.text = inp.substr(comment.i, parserInput.i - comment.i); + parserInput.commentStore.push(comment); + continue; + } else if (nextChar === '*') { + var haystack = inp.substr(parserInput.i); + var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//); + if (comment_search_result) { + comment = { + index: parserInput.i, + text: comment_search_result[0], + isLineComment: false + }; + parserInput.i += comment.text.length - 1; + parserInput.commentStore.push(comment); + continue; + } + } + break; + } + + if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) { + break; + } + } + + current = current.slice(length + parserInput.i - mem + curr); + currentPos = parserInput.i; + + if (!current.length) { + if (j < chunks.length - 1) + { + current = chunks[++j]; + skipWhitespace(0); // skip space at the beginning of a chunk + return true; // things changed + } + parserInput.finished = true; + } + + return oldi !== parserInput.i || oldj !== j; + }; + + // Same as $(), but don't change the state of the parser, + // just return the match. + parserInput.peek = function(tok) { + if (typeof(tok) === 'string') { + return input.charAt(parserInput.i) === tok; + } else { + return tok.test(current); + } + }; + + // Specialization of peek() + // TODO remove or change some currentChar calls to peekChar + parserInput.peekChar = function(tok) { + return input.charAt(parserInput.i) === tok; + }; + + parserInput.currentChar = function() { + return input.charAt(parserInput.i); + }; + + parserInput.getInput = function() { + return input; + }; + + parserInput.peekNotNumeric = function() { + var c = input.charCodeAt(parserInput.i); + //Is the first char of the dimension 0-9, '.', '+' or '-' + return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; + }; + + parserInput.getLocation = function(index, inputStream) { + inputStream = inputStream == null ? input : inputStream; + + var n = index + 1, + line = null, + column = -1; + + while (--n >= 0 && inputStream.charAt(n) !== '\n') { + column++; + } + + if (typeof index === 'number') { + line = (inputStream.slice(0, index).match(/\n/g) || "").length; + } + + return { + line: line, + column: column + }; + }; + + parserInput.start = function(str, chunkInput, parser, env) { + input = str; + parserInput.i = j = currentPos = furthest = 0; + + // chunking apparantly makes things quicker (but my tests indicate + // it might actually make things slower in node at least) + // and it is a non-perfect parse - it can't recognise + // unquoted urls, meaning it can't distinguish comments + // meaning comments with quotes or {}() in them get 'counted' + // and then lead to parse errors. + // In addition if the chunking chunks in the wrong place we might + // not be able to parse a parser statement in one go + // this is officially deprecated but can be switched on via an option + // in the case it causes too much performance issues. + if (chunkInput) { + chunks = chunker(str, function fail(msg, index) { + throw new(LessError)(parser, { + index: index, + type: 'Parse', + message: msg, + filename: env.currentFileInfo.filename + }, env); + }); + } else { + chunks = [str]; + } + + current = chunks[0]; + + skipWhitespace(0); + }; + + parserInput.end = function() { + var message, + isFinished = parserInput.i >= input.length - 1; + + if (parserInput.i < furthest) { + message = furthestPossibleErrorMessage; + parserInput.i = furthest; + } + return { + isFinished: isFinished, + furthest: parserInput.i, + furthestPossibleErrorMessage: message, + furthestReachedEnd: parserInput.i >= input.length - 1, + furthestChar: input[parserInput.i] + }; + }; + + return parserInput; +}; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index bc5052b1b..164780e12 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -1,9 +1,9 @@ -var chunker = require('./chunker.js'), - LessError = require('../less-error.js'), +var LessError = require('../less-error.js'), tree = require("../tree/index.js"), visitor = require("../visitor/index.js"), contexts = require("../contexts.js"), - getImportManager = require("./imports.js"); + getImportManager = require("./imports.js"), + getParserInput = require("./parser-input.js"); module.exports = function(less) { // @@ -40,17 +40,9 @@ module.exports = function(less) { // // var Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers; + var parser, + parsers, + parserInput = getParserInput(); // Top parser on an import tree must be sure there is one "env" // which will then be passed around by reference. @@ -61,243 +53,45 @@ var Parser = function Parser(env) { var imports = this.imports = getImportManager(less, env, Parser); - function save() { - currentPos = i; - saveStack.push( { current: current, i: i, j: j }); - } - function restore(possibleErrorMessage) { - if (i > furthest) { - furthest = i; - furthestPossibleErrorMessage = possibleErrorMessage; - } - var state = saveStack.pop(); - current = state.current; - currentPos = i = state.i; - j = state.j; - } - function forget() { - saveStack.pop(); - } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - var CHARCODE_SPACE = 32, - CHARCODE_TAB = 9, - CHARCODE_LF = 10, - CHARCODE_CR = 13, - CHARCODE_FORWARD_SLASH = 47; - - var autoCommentAbsorb = true, - commentStore = []; - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c, nextChar, comment; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - - if (autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) { - nextChar = inp[i + 1]; - if (nextChar === '/') { - comment = {index: i, isLineComment: true}; - var nextNewLine = inp.indexOf("\n", i + 1); - if (nextNewLine < 0) { - nextNewLine = endIndex; - } - i = nextNewLine; - comment.text = inp.substr(comment.i, i - comment.i); - commentStore.push(comment); - continue; - } else if (nextChar === '*') { - var haystack = inp.substr(i); - var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//); - if (comment_search_result) { - comment = { - index: i, - text: comment_search_result[0], - isLineComment: false - }; - i += comment.text.length - 1; - commentStore.push(comment); - continue; - } - } - break; - } - - if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - function expect(arg, msg, index) { // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); + var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg); if (result) { return result; } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" + error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'" : "unexpected token")); } // Specialization of expect() function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); + if (parserInput.$char(arg)) { return arg; } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); + error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'"); } function error(msg, type) { var e = new Error(msg); - e.index = i; + e.index = parserInput.i; e.type = type || 'Syntax'; throw e; } - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - function getInput(e, env) { if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { return parser.imports.contents[e.filename]; } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; + return parserInput.getInput(); } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; } - function getDebugInfo(index, inputStream, env) { + function getDebugInfo(index) { var filename = env.currentFileInfo.filename; filename = less.environment.getAbsolutePath(env, filename); return { - lineNumber: getLocation(index, inputStream).line + 1, + lineNumber: parserInput.getLocation(index).line + 1, fileName: filename }; } @@ -315,9 +109,7 @@ var Parser = function Parser(env) { // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply // parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; + var root, error = null, globalVars, modifyVars, preText = ""; globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; @@ -329,44 +121,16 @@ var Parser = function Parser(env) { str = str.replace(/\r\n/g, '\n'); // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; + str = preText + str.replace(/^\uFEFF/, '') + modifyVars; parser.imports.contents[env.currentFileInfo.filename] = str; - // chunking apparantly makes things quicker (but my tests indicate - // it might actually make things slower in node at least) - // and it is a non-perfect parse - it can't recognise - // unquoted urls, meaning it can't distinguish comments - // meaning comments with quotes or {}() in them get 'counted' - // and then lead to parse errors. - // In addition if the chunking chunks in the wrong place we might - // not be able to parse a parser statement in one go - // this is officially deprecated but can be switched on via an option - // in the case it causes too much performance issues. - if (env.chunkInput) { - try { - chunks = chunker(str, function fail(msg, index) { - throw new(LessError)(parser, { - index: index, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - }); - } catch (ex) { - return callback(new LessError(parser, ex, env)); - } - } else { - chunks = [str]; - } - - current = chunks[0]; - // Start with the primary rule. // The whole syntax tree is held under a Ruleset node, // with the `root` property set to true, so no `{}` are // output. The callback is called when the input is parsed. try { - skipWhitespace(0); + parserInput.start(str, env.chunkInput, parser, env); + root = new(tree.Ruleset)(null, this.parsers.primary()); root.root = true; root.firstRoot = true; @@ -497,41 +261,28 @@ var Parser = function Parser(env) { // showing the line where the parse error occurred. // We split it up into two parts (the part which parsed, // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { + var endInfo = parserInput.end(); + if (!endInfo.isFinished) { - var message = "Unrecognised input"; - if (i < furthest) { - message = furthestPossibleErrorMessage || message; - i = furthest; - } + var message = endInfo.furthestPossibleErrorMessage; - if (!furthestPossibleErrorMessage) { - if (input[i] === '}') { + if (!message) { + message = "Unrecognised input"; + if (endInfo.furthestChar === '}') { message += ". Possibly missing opening '{'"; - } else if (input[i] === ')') { + } else if (endInfo.furthestChar === ')') { message += ". Possibly missing opening '('"; - } else if (i >= input.length - 1) { + } else if (endInfo.furthestReachedEnd) { message += ". Possibly missing something"; } } - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { + error = new LessError(parser, { type: "Parse", message: message, - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; + index: endInfo.furthest, + filename: env.currentFileInfo.filename + }, env); } var finish = function (e) { @@ -605,14 +356,14 @@ var Parser = function Parser(env) { primary: function () { var mixin = this.mixin, root = [], node; - while (current) + while (!parserInput.finished) { while(true) { node = this.comment(); if (!node) { break; } root.push(node); } - if (peekChar('}')) { + if (parserInput.peek('}')) { break; } node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || @@ -620,7 +371,7 @@ var Parser = function Parser(env) { if (node) { root.push(node); } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { + if (!(parserInput.$re(/^[\s\n]+/) || parserInput.$re(/^;+/))) { break; } } @@ -632,8 +383,8 @@ var Parser = function Parser(env) { // comments are collected by the main parsing mechanism and then assigned to nodes // where the current structure allows it comment: function () { - if (commentStore.length) { - var comment = commentStore.shift(); + if (parserInput.commentStore.length) { + var comment = parserInput.commentStore.shift(); return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, env.currentFileInfo); } }, @@ -648,16 +399,11 @@ var Parser = function Parser(env) { // "milky way" 'he\'s the one!' // quoted: function () { - var str, j = i, e, index = i; + var str, index = parserInput.i; - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); + str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/); if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); + return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, env.currentFileInfo); } }, @@ -667,7 +413,7 @@ var Parser = function Parser(env) { // black border-collapse // keyword: function () { - var k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); + var k = parserInput.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); if (k) { return tree.Color.fromKeyword(k) || new(tree.Keyword)(k); } @@ -684,35 +430,36 @@ var Parser = function Parser(env) { // The arguments are parsed with the `entities.arguments` parser. // call: function () { - var name, nameLC, args, alpha_ret, index = i; + var name, nameLC, args, alpha, index = parserInput.i; - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } + if (parserInput.peek(/^url\(/i)) { + return; + } + + parserInput.save(); + + name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/); + if (!name) { parserInput.forget(); return; } name = name[1]; nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; + alpha = parsers.alpha(); + if(alpha) { + return alpha; } } - $char('('); // Parse the '(' and consume whitespace. - args = this.arguments(); - if (! $char(')')) { + if (! parserInput.$char(')')) { + parserInput.restore("Could not parse call arguments or missing ')'"); return; } - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } + parserInput.forget(); + return new(tree.Call)(name, args, index, env.currentFileInfo); }, arguments: function () { var args = [], arg; @@ -723,7 +470,7 @@ var Parser = function Parser(env) { break; } args.push(arg); - if (! $char(',')) { + if (! parserInput.$char(',')) { break; } } @@ -744,11 +491,11 @@ var Parser = function Parser(env) { assignment: function () { var key, value; - key = $re(/^\w+(?=\s?=)/i); + key = parserInput.$re(/^\w+(?=\s?=)/i); if (!key) { return; } - if (!$char('=')) { + if (!parserInput.$char('=')) { return; } value = parsers.entity(); @@ -767,16 +514,16 @@ var Parser = function Parser(env) { url: function () { var value; - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { + if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) { return; } - autoCommentAbsorb = false; + parserInput.autoCommentAbsorb = false; value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; + parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - autoCommentAbsorb = true; + parserInput.autoCommentAbsorb = true; expectChar(')'); @@ -793,18 +540,18 @@ var Parser = function Parser(env) { // see `parsers.variable`. // variable: function () { - var name, index = i; + var name, index = parserInput.i; - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) { return new(tree.Variable)(name, index, env.currentFileInfo); } }, // A variable entity useing the protective {} e.g. @{var} variableCurly: function () { - var curly, index = i; + var curly, index = parserInput.i; - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { + if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) { return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); } }, @@ -819,7 +566,7 @@ var Parser = function Parser(env) { color: function () { var rgb; - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { + if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string colorCandidateString = colorCandidateString[1]; if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters @@ -835,13 +582,11 @@ var Parser = function Parser(env) { // 0.5em 95% // dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { + if (parserInput.peekNotNumeric()) { return; } - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); + var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); if (value) { return new(tree.Dimension)(value[1], value[2]); } @@ -855,7 +600,7 @@ var Parser = function Parser(env) { unicodeDescriptor: function () { var ud; - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); + ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); if (ud) { return new(tree.UnicodeDescriptor)(ud[0]); } @@ -867,19 +612,11 @@ var Parser = function Parser(env) { // `window.location.href` // javascript: function () { - var str, j = i, e; + var js, index = parserInput.i; - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); + js = parserInput.$re(/^(~)?`([^`]*)`/); + if (js) { + return new(tree.JavaScript)(js[2], index, Boolean(js[1])); } } }, @@ -892,7 +629,7 @@ var Parser = function Parser(env) { variable: function () { var name; - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; } }, // @@ -903,7 +640,7 @@ var Parser = function Parser(env) { rulesetCall: function () { var name; - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { return new tree.RulesetCall(name[1]); } }, @@ -912,14 +649,14 @@ var Parser = function Parser(env) { // extend syntax - used to extend selectors // extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; + var elements, e, index = parserInput.i, option, extendList, extend; - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } + if (!(isRule ? parserInput.$re(/^&:extend\(/) : parserInput.$re(/^:extend\(/))) { return; } do { option = null; elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { + while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) { e = this.element(); if (!e) { break; } if (elements) { elements.push(e); } else { elements = [ e ]; } @@ -931,7 +668,7 @@ var Parser = function Parser(env) { extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } while($char(",")); + } while(parserInput.$char(",")); expect(/^\)/); @@ -965,26 +702,26 @@ var Parser = function Parser(env) { // selector for now. // call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, + var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex, elements, elem, e, c, args; if (s !== '.' && s !== '#') { return; } - save(); // stop us absorbing part of an invalid selector + parserInput.save(); // stop us absorbing part of an invalid selector while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); + elemIndex = parserInput.i; + e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); if (!e) { break; } elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); + c = parserInput.$char('>'); } if (elements) { - if ($char('(')) { + if (parserInput.$char('(')) { args = this.args(true).args; expectChar(')'); } @@ -994,12 +731,12 @@ var Parser = function Parser(env) { } if (parsers.end()) { - forget(); + parserInput.forget(); return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); } } - restore(); + parserInput.restore(); }, args: function (isCall) { var parsers = parser.parsers, entities = parsers.entities, @@ -1007,16 +744,16 @@ var Parser = function Parser(env) { expressions = [], argsSemiColon = [], argsComma = [], isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - save(); + parserInput.save(); while (true) { if (isCall) { arg = parsers.detachedRuleset() || parsers.expression(); } else { - commentStore.length = 0; - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { + parserInput.commentStore.length = 0; + if (parserInput.currentChar() === '.' && parserInput.$re(/^\.{3}/)) { returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { + if (parserInput.$char(";") && !isSemiColonSeperated) { isSemiColonSeperated = true; } (isSemiColonSeperated ? argsSemiColon : argsComma) @@ -1047,7 +784,7 @@ var Parser = function Parser(env) { } if (val && val instanceof tree.Variable) { - if ($char(':')) { + if (parserInput.$char(':')) { if (expressions.length > 0) { if (isSemiColonSeperated) { error("Cannot mix ; and , as delimiter types"); @@ -1064,15 +801,15 @@ var Parser = function Parser(env) { if (isCall) { error("could not understand value for named argument"); } else { - restore(); + parserInput.restore(); returner.args = []; return returner; } } nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { + } else if (!isCall && parserInput.$re(/^\.{3}/)) { returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { + if (parserInput.$char(";") && !isSemiColonSeperated) { isSemiColonSeperated = true; } (isSemiColonSeperated ? argsSemiColon : argsComma) @@ -1090,11 +827,11 @@ var Parser = function Parser(env) { argsComma.push({ name:nameLoop, value:value }); - if ($char(',')) { + if (parserInput.$char(',')) { continue; } - if ($char(';') || isSemiColonSeperated) { + if (parserInput.$char(';') || isSemiColonSeperated) { if (expressionContainsNamed) { error("Cannot mix ; and , as delimiter types"); @@ -1113,7 +850,7 @@ var Parser = function Parser(env) { } } - forget(); + parserInput.forget(); returner.args = isSemiColonSeperated ? argsSemiColon : argsComma; return returner; }, @@ -1138,14 +875,14 @@ var Parser = function Parser(env) { // definition: function () { var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { + if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') || + parserInput.peek(/^[^{]*\}/)) { return; } - save(); + parserInput.save(); - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); + match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); if (match) { name = match[1]; @@ -1158,27 +895,27 @@ var Parser = function Parser(env) { // also // .mixincall(@a: {rule: set;}); // so we have to be nice and restore - if (!$char(')')) { - restore("Missing closing ')'"); + if (!parserInput.$char(')')) { + parserInput.restore("Missing closing ')'"); return; } - commentStore.length = 0; + parserInput.commentStore.length = 0; - if ($re(/^when/)) { // Guard + if (parserInput.$re(/^when/)) { // Guard cond = expect(parsers.conditions, 'expected condition'); } ruleset = parsers.block(); if (ruleset) { - forget(); + parserInput.forget(); return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); } else { - restore(); + parserInput.restore(); } } else { - forget(); + parserInput.forget(); } } }, @@ -1200,7 +937,7 @@ var Parser = function Parser(env) { // it's there, if ';' was ommitted. // end: function () { - return $char(';') || peekChar('}'); + return parserInput.$char(';') || parserInput.peek('}'); }, // @@ -1211,12 +948,13 @@ var Parser = function Parser(env) { alpha: function () { var value; - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(tree.Alpha)(value); + if (! parserInput.$re(/^opacity=/i)) { return; } + value = parserInput.$re(/^\d+/); + if (!value) { + value = expect(this.entities.variable, "Could not parse alpha"); } + expectChar(')'); + return new(tree.Alpha)(value); }, // @@ -1232,25 +970,25 @@ var Parser = function Parser(env) { // and an element name, such as a tag a class, or `*`. // element: function () { - var e, c, v, index = i; + var e, c, v, index = parserInput.i; c = this.combinator(); - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || + e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || + parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^()@]+\)/) || parserInput.$re(/^[\.#](?=@)/) || this.entities.variableCurly(); if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { + parserInput.save(); + if (parserInput.$char('(')) { + if ((v = this.selector()) && parserInput.$char(')')) { e = new(tree.Paren)(v); - forget(); + parserInput.forget(); } else { - restore("Missing closing ')'"); + parserInput.restore("Missing closing ')'"); } } else { - forget(); + parserInput.forget(); } } @@ -1267,27 +1005,27 @@ var Parser = function Parser(env) { // we deal with this in *combinator.js*. // combinator: function () { - var c = input.charAt(i); + var c = parserInput.currentChar(); if (c === '/') { - save(); - var slashedCombinator = $re(/^\/[a-z]+\//i); + parserInput.save(); + var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i); if (slashedCombinator) { - forget(); + parserInput.forget(); return new(tree.Combinator)(slashedCombinator); } - restore(); + parserInput.restore(); } if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (c === '^' && input.charAt(i) === '^') { + parserInput.i++; + if (c === '^' && parserInput.currentChar() === '^') { c = '^^'; - i++; + parserInput.i++; } - while (isWhitespace(input, i)) { i++; } + while (parserInput.isWhitespace()) { parserInput.i++; } return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { + } else if (parserInput.isWhitespace(-1)) { return new(tree.Combinator)(" "); } else { return new(tree.Combinator)(null); @@ -1309,9 +1047,9 @@ var Parser = function Parser(env) { // Selectors are made out of one or more Elements, see above. // selector: function (isLess) { - var index = i, elements, extendList, c, e, extend, when, condition; + var index = parserInput.i, elements, extendList, c, e, extend, when, condition; - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { + while ((isLess && (extend = this.extend())) || (isLess && (when = parserInput.$re(/^when/))) || (e = this.element())) { if (when) { condition = expect(this.conditions, 'expected condition'); } else if (condition) { @@ -1320,7 +1058,7 @@ var Parser = function Parser(env) { if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } } else { if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); + c = parserInput.currentChar(); if (elements) { elements.push(e); } else { elements = [ e ]; } e = null; } @@ -1333,7 +1071,7 @@ var Parser = function Parser(env) { if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } }, attribute: function () { - if (! $char('[')) { return; } + if (! parserInput.$char('[')) { return; } var entities = this.entities, key, val, op; @@ -1342,9 +1080,9 @@ var Parser = function Parser(env) { key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); } - op = $re(/^[|~*$^]?=/); + op = parserInput.$re(/^[|~*$^]?=/); if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); + val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly(); } expectChar(']'); @@ -1358,7 +1096,7 @@ var Parser = function Parser(env) { // block: function () { var content; - if ($char('{') && (content = this.primary()) && $char('}')) { + if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) { return content; } }, @@ -1385,10 +1123,10 @@ var Parser = function Parser(env) { ruleset: function () { var selectors, s, rules, debugInfo; - save(); + parserInput.save(); if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); + debugInfo = getDebugInfo(parserInput.i); } while (true) { @@ -1397,34 +1135,34 @@ var Parser = function Parser(env) { break; } if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - commentStore.length = 0; + parserInput.commentStore.length = 0; if (s.condition && selectors.length > 1) { error("Guards are only currently allowed on a single selector."); } - if (! $char(',')) { break; } + if (! parserInput.$char(',')) { break; } if (s.condition) { error("Guards are only currently allowed on a single selector."); } - commentStore.length = 0; + parserInput.commentStore.length = 0; } if (selectors && (rules = this.block())) { - forget(); + parserInput.forget(); var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); if (env.dumpLineNumbers) { ruleset.debugInfo = debugInfo; } return ruleset; } else { - restore(); + parserInput.restore(); } }, rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; + var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable; if (c === '.' || c === '#' || c === '&') { return; } - save(); + parserInput.save(); name = this.variable() || this.ruleProperty(); if (name) { @@ -1435,38 +1173,49 @@ var Parser = function Parser(env) { } if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - // 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 merge = !isVariable && name.pop().value; + + // prefer to try to parse first if its a variable or we are compressing + // but always fallback on the other one + var tryValueFirst = !tryAnonymous && (env.compress || isVariable); + + if (tryValueFirst) { + value = this.value(); + } + if (!value) { + value = this.anonymousValue(); + if (value) { + parserInput.forget(); + // anonymous values absorb the end ';' which is reequired for them to work + return new (tree.Rule)(name, value, false, merge, startOfRule, env.currentFileInfo); + } + } + if (!tryValueFirst && !value) { + value = this.value(); + } + + important = this.important(); } if (value && this.end()) { - forget(); + parserInput.forget(); return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); } else { - restore(); + parserInput.restore(); if (value && !tryAnonymous) { return this.rule(true); } } } else { - forget(); + parserInput.forget(); } }, anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); + var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/); if (match) { - i += match[0].length - 1; return new(tree.Anonymous)(match[1]); } }, @@ -1482,9 +1231,9 @@ var Parser = function Parser(env) { // stored in `import`, which we pass to the Import constructor. // "import": function () { - var path, features, index = i; + var path, features, index = parserInput.i; - var dir = $re(/^@import?\s+/); + var dir = parserInput.$re(/^@import?\s+/); if (dir) { var options = (dir ? this.importOptions() : null) || {}; @@ -1492,8 +1241,8 @@ var Parser = function Parser(env) { if ((path = this.entities.quoted() || this.entities.url())) { features = this.mediaFeatures(); - if (!$(';')) { - i = index; + if (!parserInput.$(';')) { + parserInput.i = index; error("missing semi-colon or unrecognised media features on import"); } features = features && new(tree.Value)(features); @@ -1501,7 +1250,7 @@ var Parser = function Parser(env) { } else { - i = index; + parserInput.i = index; error("malformed import statement"); } } @@ -1511,7 +1260,7 @@ var Parser = function Parser(env) { var o, options = {}, optionName, value; // list of options, surrounded by parens - if (! $char('(')) { return null; } + if (! parserInput.$char('(')) { return null; } do { o = this.importOption(); if (o) { @@ -1528,7 +1277,7 @@ var Parser = function Parser(env) { break; } options[optionName] = value; - if (! $char(',')) { break; } + if (! parserInput.$char(',')) { break; } } } while (o); expectChar(')'); @@ -1536,7 +1285,7 @@ var Parser = function Parser(env) { }, importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); + var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference)/); if (opt) { return opt[1]; } @@ -1544,31 +1293,31 @@ var Parser = function Parser(env) { mediaFeature: function () { var entities = this.entities, nodes = [], e, p; - save(); + parserInput.save(); do { e = entities.keyword() || entities.variable(); if (e) { nodes.push(e); - } else if ($char('(')) { + } else if (parserInput.$char('(')) { p = this.property(); e = this.value(); - if ($char(')')) { + if (parserInput.$char(')')) { if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); + nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, env.currentFileInfo, true))); } else if (e) { nodes.push(new(tree.Paren)(e)); } else { - restore("badly formed media feature definition"); + parserInput.restore("badly formed media feature definition"); return null; } } else { - restore("Missing closing ')'"); + parserInput.restore("Missing closing ')'"); return null; } } } while (e); - forget(); + parserInput.forget(); if (nodes.length > 0) { return new(tree.Expression)(nodes); } @@ -1580,12 +1329,12 @@ var Parser = function Parser(env) { e = this.mediaFeature(); if (e) { features.push(e); - if (! $char(',')) { break; } + if (! parserInput.$char(',')) { break; } } else { e = entities.variable(); if (e) { features.push(e); - if (! $char(',')) { break; } + if (! parserInput.$char(',')) { break; } } } } while (e); @@ -1597,15 +1346,15 @@ var Parser = function Parser(env) { var features, rules, media, debugInfo; if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); + debugInfo = getDebugInfo(parserInput.i); } - if ($re(/^@media/)) { + if (parserInput.$re(/^@media/)) { features = this.mediaFeatures(); rules = this.block(); if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); + media = new(tree.Media)(rules, features, parserInput.i, env.currentFileInfo); if (env.dumpLineNumbers) { media.debugInfo = debugInfo; } @@ -1620,19 +1369,19 @@ var Parser = function Parser(env) { // @charset "utf-8"; // directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, + var index = parserInput.i, name, value, rules, nonVendorSpecificName, hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - if (input.charAt(i) !== '@') { return; } + if (parserInput.currentChar() !== '@') { return; } value = this['import']() || this.media(); if (value) { return value; } - save(); + parserInput.save(); - name = $re(/^@[a-z-]+/); + name = parserInput.$re(/^@[a-z-]+/); if (!name) { return; } @@ -1694,7 +1443,7 @@ var Parser = function Parser(env) { error("expected " + name + " expression"); } } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); + value = (parserInput.$re(/^[^{;]+/) || '').trim(); if (value) { value = new(tree.Anonymous)(value); } @@ -1704,13 +1453,13 @@ var Parser = function Parser(env) { rules = this.blockRuleset(); } - if (rules || (!hasBlock && value && $char(';'))) { - forget(); + if (rules || (!hasBlock && value && parserInput.$char(';'))) { + parserInput.forget(); return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); + env.dumpLineNumbers ? getDebugInfo(index) : null); } - restore("directive options not recognised"); + parserInput.restore("directive options not recognised"); }, // @@ -1728,7 +1477,7 @@ var Parser = function Parser(env) { e = this.expression(); if (e) { expressions.push(e); - if (! $char(',')) { break; } + if (! parserInput.$char(',')) { break; } } } while(e); @@ -1737,14 +1486,14 @@ var Parser = function Parser(env) { } }, important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); + if (parserInput.currentChar() === '!') { + return parserInput.$re(/^! *important/); } }, sub: function () { var a, e; - if ($char('(')) { + if (parserInput.$char('(')) { a = this.addition(); if (a) { e = new(tree.Expression)([a]); @@ -1758,27 +1507,27 @@ var Parser = function Parser(env) { var m, a, op, operation, isSpaced; m = this.operand(); if (m) { - isSpaced = isWhitespace(input, i - 1); + isSpaced = parserInput.isWhitespace(-1); while (true) { - if (peek(/^\/[*\/]/)) { + if (parserInput.peek(/^\/[*\/]/)) { break; } - save(); + parserInput.save(); - op = $char('/') || $char('*'); + op = parserInput.$char('/') || parserInput.$char('*'); - if (!op) { forget(); break; } + if (!op) { parserInput.forget(); break; } a = this.operand(); - if (!a) { restore(); break; } - forget(); + if (!a) { parserInput.restore(); break; } + parserInput.forget(); m.parensInOp = true; a.parensInOp = true; operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); + isSpaced = parserInput.isWhitespace(-1); } return operation || m; } @@ -1787,9 +1536,9 @@ var Parser = function Parser(env) { var m, a, op, operation, isSpaced; m = this.multiplication(); if (m) { - isSpaced = isWhitespace(input, i - 1); + isSpaced = parserInput.isWhitespace(-1); while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); + op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-'))); if (!op) { break; } @@ -1801,18 +1550,18 @@ var Parser = function Parser(env) { m.parensInOp = true; a.parensInOp = true; operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); + isSpaced = parserInput.isWhitespace(-1); } return operation || m; } }, conditions: function () { - var a, b, index = i, condition; + var a, b, index = parserInput.i, condition; a = this.condition(); if (a) { while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { + if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) { break; } b = this.condition(); @@ -1825,14 +1574,14 @@ var Parser = function Parser(env) { } }, condition: function () { - var entities = this.entities, index = i, negate = false, + var entities = this.entities, index = parserInput.i, negate = false, a, b, c, op; - if ($re(/^not/)) { negate = true; } + if (parserInput.$re(/^not/)) { negate = true; } expectChar('('); a = this.addition() || entities.keyword() || entities.quoted(); if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); + op = parserInput.$re(/^(?:>=|<=|=<|[<=>])/); if (op) { b = this.addition() || entities.keyword() || entities.quoted(); if (b) { @@ -1844,7 +1593,7 @@ var Parser = function Parser(env) { c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); } expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; + return parserInput.$re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; } }, @@ -1853,10 +1602,12 @@ var Parser = function Parser(env) { // such as a Color, or a Variable // operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; + var entities = this.entities, negate; + + if (parserInput.peek(/^-[@\(]/)) { + negate = parserInput.$char('-'); + } - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } var o = this.sub() || entities.dimension() || entities.color() || entities.variable() || entities.call(); @@ -1889,8 +1640,8 @@ var Parser = function Parser(env) { if (e) { entities.push(e); // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); + if (!parserInput.peek(/^\/[\/*]/)) { + delim = parserInput.$char('/'); if (delim) { entities.push(new(tree.Anonymous)(delim)); } @@ -1902,30 +1653,37 @@ var Parser = function Parser(env) { } }, property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); + var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); if (name) { return name[1]; } }, ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; + var name = [], index = [], s, k; + + parserInput.save(); function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); + var i = parserInput.i, + chunk = parserInput.$re(re); + if (chunk) { + index.push(i); + return name.push(chunk[1]); } } match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { + while (true) { + if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) { + break; + } + } + + if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { //TODO remove start \s* - un-necessary + parserInput.forget(); + // at last, we have the complete match now. move forward, // convert name particles to tree objects and return: - skipWhitespace(length); if (name[0] === '') { name.shift(); index.shift(); @@ -1939,12 +1697,13 @@ var Parser = function Parser(env) { } return name; } + parserInput.restore(); } } }; parser.getInput = getInput; - parser.getLocation = getLocation; + parser.getLocation = parserInput.getLocation; return parser; }; diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js index 0693873ca..35cabab1f 100644 --- a/lib/less/tree/js-eval-node.js +++ b/lib/less/tree/js-eval-node.js @@ -10,6 +10,11 @@ jsEvalNode.prototype.evaluateJavaScript = function (expression, env) { that = this, context = {}; + if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { + throw { message: "You are using JavaScript, which has been disabled." , + index: this.index }; + } + expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { return that.jsify(new(Variable)('@' + name, that.index).eval(env)); }); diff --git a/test/less/errors/javascript-error.txt b/test/less/errors/javascript-error.txt index c4da95085..e5f7dc39e 100644 --- a/test/less/errors/javascript-error.txt +++ b/test/less/errors/javascript-error.txt @@ -1,4 +1,4 @@ -SyntaxError: JavaScript evaluation error: 'TypeError: Cannot read property 'toJS' of undefined' in {path}javascript-error.less on line 2, column 25: +SyntaxError: JavaScript evaluation error: 'TypeError: Cannot read property 'toJS' of undefined' in {path}javascript-error.less on line 2, column 10: 1 .scope { 2 var: `this.foo.toJS`; 3 } diff --git a/test/less/errors/javascript-undefined-var.txt b/test/less/errors/javascript-undefined-var.txt index b363aff9b..5fb14ee0b 100644 --- a/test/less/errors/javascript-undefined-var.txt +++ b/test/less/errors/javascript-undefined-var.txt @@ -1,4 +1,4 @@ -NameError: variable @b is undefined in {path}javascript-undefined-var.less on line 2, column 15: +NameError: variable @b is undefined in {path}javascript-undefined-var.less on line 2, column 9: 1 .scope { 2 @a: `@{b}`; 3 } From 16c4f88b7f9e0857598cb24834b9a60003d9715f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 25 Aug 2014 12:25:54 +0100 Subject: [PATCH 059/161] small optimisation/fix todo --- lib/less/parser/parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 164780e12..06a36d580 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -1679,7 +1679,7 @@ var Parser = function Parser(env) { } } - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { //TODO remove start \s* - un-necessary + if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) { parserInput.forget(); // at last, we have the complete match now. move forward, From ca72b4d92fc7fb769ffd6e2360967521666a8f69 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 26 Aug 2014 22:26:27 +0100 Subject: [PATCH 060/161] Clean up dependency injection so environment is injected at the top level --- lib/less/browser.js | 25 +++++++++++++++++-------- lib/less/environment/browser.js | 8 ++++---- lib/less/functions/data-uri.js | 14 +++++++------- lib/less/functions/index.js | 6 +++--- lib/less/functions/svg.js | 4 ++-- lib/less/index.js | 5 ++--- lib/less/non-node-index.js | 26 +++++++++++++------------- lib/less/parser/imports.js | 12 ++++++------ lib/less/parser/parser.js | 15 ++++++++------- lib/less/source-map-output.js | 6 +++--- lib/less/tree/call.js | 4 ++-- 11 files changed, 67 insertions(+), 58 deletions(-) diff --git a/lib/less/browser.js b/lib/less/browser.js index 61d769152..e2547352c 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -3,9 +3,6 @@ // /*global window, document, location */ -var less = require('./non-node-index.js'), - options = window.less; - var logLevel = { debug: 3, info: 2, @@ -13,16 +10,28 @@ var logLevel = { none: 0 }; +var less; + function log(str, level) { if (typeof(console) !== 'undefined' && less.logLevel >= level) { console.log('less: ' + str); } } -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); +/* + TODO - options is now hidden - we should expose it on the less object, but not have it "as" the less object + alternately even have it on environment + e.g. less.environment.options.fileAsync = true; + is it weird you do + less = { fileAsync: true } + then access as less.environment.options.fileAsync ? + */ + +var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), + options = window.less, + environment = require("./environment/browser.js")(options, isFileProtocol, log, logLevel); -less.environment = require("./environment/browser.js")(less, isFileProtocol, log, logLevel); -window.less = less; +window.less = less = require('./non-node-index.js')(environment); less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || @@ -46,8 +55,8 @@ less.logLevel = typeof(options.logLevel) != 'undefined' ? options.logLevel : (le // doesn't start loading before the stylesheets are parsed. // Setting this to `true` can result in flickering. // -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; +options.async = options.async || false; +options.fileAsync = options.fileAsync || false; // Interval between watch polls less.poll = less.poll || (isFileProtocol ? 1000 : 1500); diff --git a/lib/less/environment/browser.js b/lib/less/environment/browser.js index 30d2b5c85..85a9f646d 100644 --- a/lib/less/environment/browser.js +++ b/lib/less/environment/browser.js @@ -1,10 +1,10 @@ /*global window, XMLHttpRequest */ -module.exports = function(less, isFileProtocol, log, logLevel) { +module.exports = function(options, isFileProtocol, log, logLevel) { var fileCache = {}; -//TODOS - move log somewhere. pathDiff and doing something similiar in node. use pathDiff in the other browser file for the initial load +//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load // isFileProtocol is global function getXMLHttpRequest() { @@ -135,7 +135,7 @@ return { doXHR: function doXHR(url, type, callback, errback) { var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; + var async = isFileProtocol ? options.fileAsync : options.async; if (typeof(xhr.overrideMimeType) === 'function') { xhr.overrideMimeType('text/css'); @@ -154,7 +154,7 @@ return { } } - if (isFileProtocol && !less.fileAsync) { + if (isFileProtocol && !options.fileAsync) { if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { callback(xhr.responseText); } else { diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index 47e1273e5..ad5ac6542 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -1,11 +1,11 @@ -module.exports = function(less) { +module.exports = function(environment) { var Anonymous = require("../tree/anonymous.js"), URL = require("../tree/url.js"), functionRegistry = require("./function-registry.js"); functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { - if (!less.environment.supportsDataURI(this.env)) { + if (!environment.supportsDataURI(this.env)) { return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); } @@ -27,19 +27,19 @@ module.exports = function(less) { if (this.env.isPathRelative(filePath)) { if (this.currentFileInfo.relativeUrls) { - filePath = less.environment.join(this.currentFileInfo.currentDirectory, filePath); + filePath = environment.join(this.currentFileInfo.currentDirectory, filePath); } else { - filePath = less.environment.join(this.currentFileInfo.entryPath, filePath); + filePath = environment.join(this.currentFileInfo.entryPath, filePath); } } // detect the mimetype if not given if (arguments.length < 2) { - mimetype = less.environment.mimeLookup(this.env, filePath); + mimetype = environment.mimeLookup(this.env, filePath); // use base 64 unless it's an ASCII or UTF-8 format - var charset = less.environment.charsetLookup(this.env, mimetype); + var charset = environment.charsetLookup(this.env, mimetype); useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; if (useBase64) { mimetype += ';base64'; } } @@ -47,7 +47,7 @@ module.exports = function(less) { useBase64 = /;base64$/.test(mimetype); } - var buf = less.environment.readFileSync(filePath); + var buf = environment.readFileSync(filePath); // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded // and the --ieCompat flag is enabled, return a normal url() instead. diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js index 261b4bc57..ab605b6ca 100644 --- a/lib/less/functions/index.js +++ b/lib/less/functions/index.js @@ -1,4 +1,4 @@ -module.exports = function(less) { +module.exports = function(environment) { var functions = { functionRegistry: require("./function-registry.js"), functionCaller: require("./function-caller.js") @@ -8,11 +8,11 @@ module.exports = function(less) { require("./default.js"); require("./color.js"); require("./color-blending.js"); - require("./data-uri.js")(less); + require("./data-uri.js")(environment); require("./math.js"); require("./number.js"); require("./string.js"); - require("./svg.js")(less); + require("./svg.js")(environment); require("./types.js"); return functions; diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js index 439a2184b..2927f0b34 100644 --- a/lib/less/functions/svg.js +++ b/lib/less/functions/svg.js @@ -1,4 +1,4 @@ -module.exports = function(less) { +module.exports = function(environment) { var Dimension = require("../tree/dimension.js"), Color = require("../tree/color.js"), Anonymous = require("../tree/anonymous.js"), @@ -71,7 +71,7 @@ module.exports = function(less) { if (useBase64) { try { - returner = less.environment.encodeBase64(this.env, returner); + returner = environment.encodeBase64(this.env, returner); } catch(e) { useBase64 = false; } diff --git a/lib/less/index.js b/lib/less/index.js index 5d4a0bd13..c83153c59 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,5 +1,6 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; -var less = require("./non-node-index.js"); +var environment = require("./environment/node"); +var less = require("./non-node-index.js")(environment); less.render = function (input, options, callback) { options = options || {}; @@ -87,6 +88,4 @@ less.writeError = function (ctx, options) { console.error(less.formatError(ctx, options)); }; -less.environment = require("./environment/node"); - module.exports = less; diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 9279f8d14..0a618c3e9 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -1,14 +1,14 @@ -var less = { - version: [1, 6, 3], - data: require('./data/index.js') -}; - -less.tree = require('./tree/index.js'); -less.visitor = require('./visitor/index.js'); -less.Parser = (require('./parser/parser.js'))(less); -less.functions = require('./functions/index.js')(less); -less.contexts = require("./contexts.js"); +module.exports = function(environment) { + var less = { + version: [2, 0, 0], + data: require('./data/index.js'), + tree: require('./tree/index.js'), + visitor: require('./visitor/index.js'), + Parser: require('./parser/parser.js')(environment), + functions: require('./functions/index.js')(environment), + contexts: require("./contexts.js"), + environment: environment + }; -less.SourceMapOutput = require('./source-map-output.js')(less); - -module.exports = less; + return less; +}; diff --git a/lib/less/parser/imports.js b/lib/less/parser/imports.js index bec1ce9e6..2b7c4efad 100644 --- a/lib/less/parser/imports.js +++ b/lib/less/parser/imports.js @@ -1,5 +1,5 @@ var contexts = require("../contexts.js"); -module.exports = function(less, env, Parser) { +module.exports = function(environment, env, Parser) { var rootFilename = env && env.filename; return { paths: env.paths || [], // Search paths, when importing @@ -32,7 +32,7 @@ module.exports = function(less, env, Parser) { rootFilename: currentFileInfo.rootFilename }; - less.environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { + environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { if (e) { fileParsedFunc(e); return; @@ -46,11 +46,11 @@ module.exports = function(less, env, Parser) { // then rootpath should become 'less/module/nav/' // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' - newFileInfo.currentDirectory = less.environment.getPath(env, resolvedFilename); + newFileInfo.currentDirectory = environment.getPath(env, resolvedFilename); if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = less.environment.join((env.rootpath || ""), less.environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); - if (!less.environment.isPathAbsolute(env, newFileInfo.rootpath) && less.environment.alwaysMakePathsAbsolute()) { - newFileInfo.rootpath = less.environment.join(newFileInfo.entryPath, newFileInfo.rootpath); + newFileInfo.rootpath = environment.join((env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!environment.isPathAbsolute(env, newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath); } } newFileInfo.filename = resolvedFilename; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 06a36d580..8691ec5e2 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -5,7 +5,8 @@ var LessError = require('../less-error.js'), getImportManager = require("./imports.js"), getParserInput = require("./parser-input.js"); -module.exports = function(less) { +module.exports = function(environment) { +var SourceMapOutput = require("../source-map-output")(environment); // // less.js - parser // @@ -51,7 +52,7 @@ var Parser = function Parser(env) { } this.env = env; - var imports = this.imports = getImportManager(less, env, Parser); + var imports = this.imports = getImportManager(environment, env, Parser); function expect(arg, msg, index) { // some older browsers return typeof 'function' for RegExp @@ -88,7 +89,7 @@ var Parser = function Parser(env) { function getDebugInfo(index) { var filename = env.currentFileInfo.filename; - filename = less.environment.getAbsolutePath(env, filename); + filename = environment.getAbsolutePath(env, filename); return { lineNumber: parserInput.getLocation(index).line + 1, @@ -111,8 +112,8 @@ var Parser = function Parser(env) { parse: function (str, callback, additionalData) { var root, error = null, globalVars, modifyVars, preText = ""; - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; + globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : ''; + modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : ''; if (globalVars || (additionalData && additionalData.banner)) { preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; @@ -206,7 +207,7 @@ var Parser = function Parser(env) { } if (options.sourceMap) { - evaldRoot = new less.SourceMapOutput( + evaldRoot = new SourceMapOutput( { contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, writeSourceMap: options.writeSourceMap, @@ -231,7 +232,7 @@ var Parser = function Parser(env) { throw new LessError(parser, e, env); } - var CleanCSS = less.environment.getCleanCSS(); + var CleanCSS = environment.getCleanCSS(); if (options.cleancss && CleanCSS) { var cleancssOptions = options.cleancssOptions || {}; diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 1bb1fac2d..a70889ba2 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -1,4 +1,4 @@ -module.exports = function (less) { +module.exports = function (environment) { var SourceMapOutput = function (options) { this._css = []; @@ -14,7 +14,7 @@ module.exports = function (less) { } this._sourceMapRootpath = options.sourceMapRootpath; this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = less.environment.getSourceMapGenerator(); + this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator(); if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { this._sourceMapRootpath += '/'; @@ -127,7 +127,7 @@ module.exports = function (less) { if (this._writeSourceMap) { this._writeSourceMap(sourceMapContent); } else { - sourceMapURL = "data:application/json;base64," + less.environment.encodeBase64(sourceMapContent); + sourceMapURL = "data:application/json;base64," + environment.encodeBase64(sourceMapContent); } if (sourceMapURL) { diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index f7cbfed6d..652194995 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -1,4 +1,5 @@ -var Node = require("./node.js"); +var Node = require("./node.js"), + FunctionCaller = require("../functions/function-caller.js"); // // A function call node. // @@ -30,7 +31,6 @@ Call.prototype.accept = function (visitor) { // Call.prototype.eval = function (env) { var args = this.args.map(function (a) { return a.eval(env); }), - FunctionCaller = require("../non-node-index.js").functions.functionCaller, //TODO! Move out result, funcCaller = new FunctionCaller(this.name, env, this.currentFileInfo); if (funcCaller.isValid()) { // 1. From a0658a4b1533ab6bdaf2ddea6210134d0cd07dc6 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 3 Sep 2014 15:07:33 +0100 Subject: [PATCH 061/161] move render to be accessible in all environments and start using it in the browser --- lib/less/browser.js | 59 +++++++++++++++++++++++++------------- lib/less/index.js | 31 -------------------- lib/less/non-node-index.js | 1 + lib/less/render.js | 34 ++++++++++++++++++++++ 4 files changed, 74 insertions(+), 51 deletions(-) create mode 100644 lib/less/render.js diff --git a/lib/less/browser.js b/lib/less/browser.js index e2547352c..0b4c6885f 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -312,37 +312,56 @@ function removeError(path) { } } +function clone(obj) { + var cloned = {}; + for(var prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + return cloned; +} + +// only really needed for phantom +function bind(func, thisArg) { + var curryArgs = Array.prototype.slice.call(arguments, 2); + return function() { + var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0)); + return func.apply(thisArg, args); + }; +} + function loadStyles(modifyVars) { var styles = document.getElementsByTagName('style'), style; + for (var i = 0; i < styles.length; i++) { style = styles[i]; if (style.type.match(typePattern)) { - var env = new less.contexts.parseEnv(options), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); + var instanceOptions = clone(options); + instanceOptions.modifyVars = modifyVars; + var lessText = style.innerHTML || ''; + instanceOptions.filename = document.location.href.replace(/#.*$/, ''); - if (modifyVars || options.globalVars) { - env.useFileCache = true; + if (modifyVars || instanceOptions.globalVars) { + instanceOptions.useFileCache = true; } /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(options); + // use closure to store current style + less.render(lessText, instanceOptions) + .then( + bind(function(style, css) { style.type = 'text/css'; if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.innerHTML = css; } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: options.globalVars, modifyVars: modifyVars}); + }, null, style), + function(e) { + error(e, "inline"); + }); } } } @@ -417,7 +436,7 @@ function initRunningMode(){ if (e) { error(e, sheet.href); } else if (root) { - var styles = root.toCSS(less); + var styles = root.toCSS(options); styles = postProcessCSS(styles); createCSS(styles, sheet, env.lastModified); } @@ -477,20 +496,20 @@ less.refresh = function (reload, modifyVars) { var startTime, endTime; startTime = endTime = new Date(); - loadStyleSheets(function (e, root, _, sheet, env) { + loadStyleSheets(function (e, root, _, sheet, webInfo) { if (e) { return error(e, sheet.href); } - if (env.local) { + if (webInfo.local) { log("loading " + sheet.href + " from cache.", logLevel.info); } else { log("parsed " + sheet.href + " successfully.", logLevel.debug); var styles = root.toCSS(options); styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); + createCSS(styles, sheet, webInfo.lastModified); } log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { + if (webInfo.remaining === 0) { log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); } endTime = new Date(); diff --git a/lib/less/index.js b/lib/less/index.js index f764f66e5..cb4719f33 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,37 +1,6 @@ -var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; var environment = require("./environment/node"); var less = require("./non-node-index.js")(environment); -less.render = function (input, options, callback) { - options = options || {}; - - if (typeof(options) === 'function') { - callback = options; - options = {}; - } - - var parser = new(less.Parser)(options); - - if (callback) { - parser.parse(input, function (e, root) { - if (e) { callback(e); return; } - var css; - try { - css = root && root.toCSS && root.toCSS(options); - } - catch (err) { callback(err); return; } - callback(null, css); - }, options); - } else { - return new PromiseConstructor(function (resolve, reject) { - parser.parse(input, function (e, root) { - if (e) { return reject(e); } - try { resolve(root.toCSS(options)); } - catch (err) { reject( err); } - }, options); - }); - } -}; less.formatError = function(ctx, options) { options = options || {}; diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 0a618c3e9..0e43bc0b0 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -9,6 +9,7 @@ module.exports = function(environment) { contexts: require("./contexts.js"), environment: environment }; + less.render = require("./render.js")(less.Parser); return less; }; diff --git a/lib/less/render.js b/lib/less/render.js new file mode 100644 index 000000000..d4ea7cc5f --- /dev/null +++ b/lib/less/render.js @@ -0,0 +1,34 @@ +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; + +module.exports = function(Parser) { + return function (input, options, callback) { + options = options || {}; + + if (typeof(options) === 'function') { + callback = options; + options = {}; + } + + var parser = new(Parser)(options); + + if (callback) { + parser.parse(input, function (e, root) { + if (e) { callback(e); return; } + var css; + try { + css = root && root.toCSS && root.toCSS(options); + } + catch (err) { callback(err); return; } + callback(null, css); + }, options); + } else { + return new PromiseConstructor(function (resolve, reject) { + parser.parse(input, function (e, root) { + if (e) { return reject(e); } + try { resolve(root.toCSS(options)); } + catch (err) { reject( err); } + }, options); + }); + } + }; +}; From d0b6c50aef1df6899dd34e756339939f8596dbd4 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 3 Sep 2014 16:23:23 +0100 Subject: [PATCH 062/161] missing rename in gruntfile --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index a5000b875..9a70c8d42 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -278,7 +278,7 @@ module.exports = function (grunt) { // setup a web server to run the browser tests in a browser rather than phantom grunt.registerTask('browsertest-server', [ - 'browsertest-less', + 'browsertest-lessjs', 'shell:browsertest-server' ]); From 544bd3abf084f7ab5586d7b578d3e37494cebc31 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 3 Sep 2014 16:24:09 +0100 Subject: [PATCH 063/161] Change code to use less.render --- bin/lessc | 85 ++++++++++++++++++++------------------------- lib/less/browser.js | 55 ++++++++++++++--------------- test/less-test.js | 34 ++++++------------ 3 files changed, 76 insertions(+), 98 deletions(-) diff --git a/bin/lessc b/bin/lessc index 59defad0e..f9e0b70dc 100755 --- a/bin/lessc +++ b/bin/lessc @@ -342,60 +342,51 @@ var parseLessFile = function (e, data) { options.paths = [path.dirname(input)].concat(options.paths); options.filename = input; - var parser = new(less.Parser)(options); - parser.parse(data, function (err, tree) { - if (err) { - less.writeError(err, options); - currentErrorcode = 1; - return; - } else if (options.depends) { + if (options.depends) { + var parser = new(less.Parser)(options); + parser.parse(data, function (err, tree) { + if (err) { + less.writeError(err, options); + currentErrorcode = 1; + return; + } for(var file in parser.imports.files) { if (parser.imports.files.hasOwnProperty(file)) { process.stdout.write(file + " "); } } console.log(""); - } else { - try { - if (options.lint) { writeSourceMap = function() {}; } - var css = tree.toCSS({ - silent: options.silent, - verbose: options.verbose, - ieCompat: options.ieCompat, - compress: options.compress, - cleancss: options.cleancss, - cleancssOptions: cleancssOptions, - sourceMap: Boolean(options.sourceMap), - sourceMapFilename: options.sourceMap, - sourceMapURL: options.sourceMapURL, - sourceMapOutputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath || "", - outputSourceFiles: options.outputSourceFiles, - writeSourceMap: writeSourceMap, - maxLineLen: options.maxLineLen, - strictMath: options.strictMath, - strictUnits: options.strictUnits, - urlArgs: options.urlArgs - }); - if(!options.lint) { - if (output) { - ensureDirectory(output); - fs.writeFileSync(output, css, 'utf8'); - if (options.verbose) { - console.log('lessc: wrote ' + output); - } - } else { - process.stdout.write(css); - } - } - } catch (e) { - less.writeError(e, options); - currentErrorcode = 2; - return; + }); + return; + } + + if (options.lint) { + options.writeSourceMap = function() {}; + } else { + options.writeSourceMap = writeSourceMap; + } + options.sourceMapFilename = options.sourceMap; + options.sourceMap = Boolean(options.sourceMap); + options.sourceMapRootpath = options.sourceMapRootpath || ""; + + less.render(data, options) + .then(function(css) { + if(!options.lint) { + if (output) { + ensureDirectory(output); + fs.writeFileSync(output, css, 'utf8'); + if (options.verbose) { + console.log('lessc: wrote ' + output); + } + } else { + process.stdout.write(css); + } } - } - }); + }, + function(err) { + less.writeError(err, options); + currentErrorcode = 1; + }); }; if (input != '-') { diff --git a/lib/less/browser.js b/lib/less/browser.js index 0b4c6885f..5b9e9e0d7 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -350,8 +350,7 @@ function loadStyles(modifyVars) { /*jshint loopfunc:true */ // use closure to store current style less.render(lessText, instanceOptions) - .then( - bind(function(style, css) { + .then(bind(function(style, css) { style.type = 'text/css'; if (style.styleSheet) { style.styleSheet.cssText = css; @@ -368,23 +367,26 @@ function loadStyles(modifyVars) { function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - var env = new less.contexts.parseEnv(options); - env.mime = sheet.type; + var instanceOptions = clone(options); + instanceOptions.mime = sheet.type; + if (modifyVars) { + instanceOptions.modifyVars = modifyVars; + } if (modifyVars || options.globalVars) { - env.useFileCache = true; + instanceOptions.useFileCache = true; } - less.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { + less.environment.loadFile(instanceOptions, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { var newFileInfo = { - currentDirectory: less.environment.getPath(env, path), + currentDirectory: less.environment.getPath(instanceOptions, path), filename: path, rootFilename: path, - relativeUrls: env.relativeUrls}; + relativeUrls: instanceOptions.relativeUrls}; newFileInfo.entryPath = newFileInfo.currentDirectory; - newFileInfo.rootpath = env.rootpath || newFileInfo.currentDirectory; + newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory; if (webInfo) { webInfo.remaining = remaining; @@ -407,19 +409,18 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { removeError(path); if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { + instanceOptions.currentFileInfo = newFileInfo; + less.render(data, instanceOptions) + .then(function(css) { + callback(null, css, data, sheet, webInfo, path); + }, + function(e) { callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: options.globalVars}); + }); } else { callback(e, null, null, sheet, webInfo, path); } - }, env, modifyVars); + }); } function loadStyleSheets(callback, reload, modifyVars) { @@ -432,13 +433,12 @@ function initRunningMode(){ if (less.env === 'development') { less.watchTimer = setInterval(function () { if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { + loadStyleSheets(function (e, css, _, sheet, env) { if (e) { error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(options); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); + } else if (css) { + css = postProcessCSS(css); + createCSS(css, sheet, env.lastModified); } }); } @@ -496,17 +496,16 @@ less.refresh = function (reload, modifyVars) { var startTime, endTime; startTime = endTime = new Date(); - loadStyleSheets(function (e, root, _, sheet, webInfo) { + loadStyleSheets(function (e, css, _, sheet, webInfo) { if (e) { return error(e, sheet.href); } if (webInfo.local) { log("loading " + sheet.href + " from cache.", logLevel.info); } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(options); - styles = postProcessCSS(styles); - createCSS(styles, sheet, webInfo.lastModified); + log("rendered " + sheet.href + " successfully.", logLevel.debug); + css = postProcessCSS(css); + createCSS(css, sheet, webInfo.lastModified); } log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); if (webInfo.remaining === 0) { diff --git a/test/less-test.js b/test/less-test.js index 22e45a0b8..de13db7df 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -203,7 +203,6 @@ module.exports = function() { } function toCSS(options, path, callback) { - var css; options = options || {}; fs.readFile(path, 'utf8', function (e, str) { if (e) { return callback(e); } @@ -212,31 +211,20 @@ module.exports = function() { options.filename = require('path').resolve(process.cwd(), path); options.optimization = options.optimization || 0; - var parser = new(less.Parser)(options); - var additionalData = {}; if (options.globalVars) { - additionalData.globalVars = options.getVars(path); + options.globalVars = options.getVars(path); } else if (options.modifyVars) { - additionalData.modifyVars = options.getVars(path); + options.modifyVars = options.getVars(path); } - if (options.banner) { - additionalData.banner = options.banner; - } - parser.parse(str, function (err, tree) { - if (err) { - callback(err); - } else { - try { - css = tree.toCSS(options); - var css2 = tree.toCSS(options); // integration test that 2nd call gets same output - if (css2 !== css) { throw new Error("css not equal to 2nd call"); } - callback(null, css); - } catch (e) { - callback(e); - } - } - }, additionalData); - }); + + less.render(str, options) + .then(function(css) { + // TODO integration test that calling toCSS twice results in the same css? + callback(null, css); + }, function(e) { + callback(e); + }); + }); } function testNoOptions() { From b55b596740fb9004c6b86d9348a07c854ad1a01e Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 3 Sep 2014 18:21:25 +0100 Subject: [PATCH 064/161] move transform out of the parser --- lib/less/parser/parser.js | 72 +++++--------------------------------- lib/less/transform-tree.js | 71 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 64 deletions(-) create mode 100644 lib/less/transform-tree.js diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index dfc6acee0..d870c8fcf 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -140,72 +140,16 @@ var Parser = function Parser(env) { } root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new contexts.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; + return function (options) { + var transformTree = require("../transform-tree.js"), + evaldRoot; + try { + evaldRoot = transformTree(this, options); + } catch (e) { + throw new LessError(parser, e, env); } - + var css; try { - var preEvalVisitors = [], - visitors = [ - new(visitor.JoinSelectorVisitor)(), - new(visitor.ExtendVisitor)(), - new(visitor.ToCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - if (options.sourceMap) { evaldRoot = new SourceMapOutput( { diff --git a/lib/less/transform-tree.js b/lib/less/transform-tree.js new file mode 100644 index 000000000..bbfba9e98 --- /dev/null +++ b/lib/less/transform-tree.js @@ -0,0 +1,71 @@ +var contexts = require("./contexts.js"), + visitor = require("./visitor/index.js"), + tree = require("./tree/index.js"); + +module.exports = function(root, options) { + options = options || {}; + var evaldRoot, + variables = options.variables, + evalEnv = new contexts.evalEnv(options); + + // + // Allows setting variables with a hash, so: + // + // `{ color: new(tree.Color)('#f01') }` will become: + // + // new(tree.Rule)('@color', + // new(tree.Value)([ + // new(tree.Expression)([ + // new(tree.Color)('#f01') + // ]) + // ]) + // ) + // + if (typeof(variables) === 'object' && !Array.isArray(variables)) { + variables = Object.keys(variables).map(function (k) { + var value = variables[k]; + + if (! (value instanceof tree.Value)) { + if (! (value instanceof tree.Expression)) { + value = new(tree.Expression)([value]); + } + value = new(tree.Value)([value]); + } + return new(tree.Rule)('@' + k, value, false, null, 0); + }); + evalEnv.frames = [new(tree.Ruleset)(null, variables)]; + } + + var preEvalVisitors = [], + visitors = [ + new(visitor.JoinSelectorVisitor)(), + new(visitor.ExtendVisitor)(), + new(visitor.ToCSSVisitor)({compress: Boolean(options.compress)}) + ], i; + + if (options.plugins) { + for(i =0; i < options.plugins.length; i++) { + if (options.plugins[i].isPreEvalVisitor) { + preEvalVisitors.push(options.plugins[i]); + } else { + if (options.plugins[i].isPreVisitor) { + visitors.splice(0, 0, options.plugins[i]); + } else { + visitors.push(options.plugins[i]); + } + } + } + } + + for(i = 0; i < preEvalVisitors.length; i++) { + preEvalVisitors[i].run(root); + } + + evaldRoot = root.eval(evalEnv); + + for(i = 0; i < visitors.length; i++) { + visitors[i].run(evaldRoot); + } + + return evaldRoot; +}; From e108b50c51e0cbb0653ec82e9de83e01d8dabedc Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 3 Sep 2014 18:33:19 +0100 Subject: [PATCH 065/161] move cleancss into lessc bin only --- bin/lessc | 15 +++++++++++++++ lib/less/contexts.js | 2 -- lib/less/environment/browser.js | 2 -- lib/less/environment/node.js | 3 --- lib/less/parser/parser.js | 16 +--------------- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/bin/lessc b/bin/lessc index f9e0b70dc..9edcd9584 100755 --- a/bin/lessc +++ b/bin/lessc @@ -372,6 +372,21 @@ var parseLessFile = function (e, data) { less.render(data, options) .then(function(css) { if(!options.lint) { + if (options.cleancss) { + var CleanCSS = require('clean-css'); + var cleancssOptions = options.cleancssOptions || {}; + + if (cleancssOptions.keepSpecialComments === undefined) { + cleancssOptions.keepSpecialComments = "*"; + } + cleancssOptions.processImport = false; + cleancssOptions.noRebase = true; + if (cleancssOptions.noAdvanced === undefined) { + cleancssOptions.noAdvanced = true; + } + + css = new CleanCSS(cleancssOptions).minify(css); + } if (output) { ensureDirectory(output); fs.writeFileSync(output, css, 'utf8'); diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 269048aec..c79349e41 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -69,11 +69,9 @@ var evalCopyProperties = [ 'silent', // whether to swallow errors and warnings 'verbose', // whether to log more activity 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) 'strictMath', // whether math has to be within parenthesis 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css 'sourceMap', // whether to output a source map 'importMultiple', // whether we are currently importing multiple copies 'urlArgs', // whether to add args into url tokens diff --git a/lib/less/environment/browser.js b/lib/less/environment/browser.js index 85a9f646d..2e045c732 100644 --- a/lib/less/environment/browser.js +++ b/lib/less/environment/browser.js @@ -44,8 +44,6 @@ return { alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { return true; }, - getCleanCSS: function () { - }, supportsDataURI: function() { return false; }, diff --git a/lib/less/environment/node.js b/lib/less/environment/node.js index 884f6eb8b..9c2f8e980 100644 --- a/lib/less/environment/node.js +++ b/lib/less/environment/node.js @@ -23,9 +23,6 @@ module.exports = { readFileSync: function (filename) { return require("fs").readFileSync(filename); }, - getCleanCSS: function() { - return require('clean-css'); - }, getPath: function (env, filename) { var j = filename.lastIndexOf('/'); if (j < 0) { diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index d870c8fcf..6d19dd9cf 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -176,21 +176,7 @@ var Parser = function Parser(env) { throw new LessError(parser, e, env); } - var CleanCSS = environment.getCleanCSS(); - if (options.cleancss && CleanCSS) { - var cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { + if (options.compress) { return css.replace(/(^(\s)+)|((\s)+$)/g, ""); } else { return css; From c4e2514905d833f28a8fce23675aebbcb56ed2e5 Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Sat, 17 May 2014 00:36:38 +0400 Subject: [PATCH 066/161] reworked guard comparison --- lib/less/tree.js | 40 +++++++++++++++++++ lib/less/tree/anonymous.js | 15 +------- lib/less/tree/color.js | 9 ++--- lib/less/tree/condition.js | 28 ++++---------- lib/less/tree/dimension.js | 43 +++++++++------------ lib/less/tree/keyword.js | 9 +---- lib/less/tree/quoted.js | 22 ++--------- test/css/mixins-guards.css | 74 ++++++++++++++++++++++++++++++++++++ test/less/mixins-guards.less | 43 +++++++++++++++++++++ 9 files changed, 191 insertions(+), 92 deletions(-) diff --git a/lib/less/tree.js b/lib/less/tree.js index d7ccd9748..5814059b5 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -94,4 +94,44 @@ tree.outputRuleset = function (env, output, rules) { env.tabLevel--; }; +tree.compare = function (a, b) { + /* returns: + -1: a < b + 0: a = b + 1: a > b + and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */ + + if ((a.compare) && + // for "symmetric results" force toCSS-based comparison + // of Quoted or Anonymous if either value is one of those + !(b.type === "Quoted" || b.type === "Anonymous")) { + return a.compare(b); + } else if (b.compare) { + return -b.compare(a); + } else if (a.type !== b.type) { + return undefined; + } + + a = a.value; + b = b.value; + if (!Array.isArray(a)) { + return a === b ? 0 : undefined; + } + if (a.length !== b.length) { + return undefined; + } + for (var i = 0; i < a.length; i++) { + if (tree.compare(a[i], b[i]) !== 0) { + return undefined; + } + } + return 0; +}; + +tree.numericCompare = function (a, b) { + return a < b ? -1 + : a === b ? 0 + : a > b ? 1 : undefined; +}; + })(require('./tree')); diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 45840613b..a960de62b 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -12,19 +12,8 @@ tree.Anonymous.prototype = { eval: function () { return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; + compare: function (other) { + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; }, isRulesetLike: function() { return this.rulesetLike; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index eac71f7e0..15303bea1 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -150,14 +150,11 @@ tree.Color.prototype = { return toHex([this.alpha * 255].concat(this.rgb)); }, compare: function (x) { - if (!x.rgb) { - return -1; - } - - return (x.rgb[0] === this.rgb[0] && + return (x.rgb && + x.rgb[0] === this.rgb[0] && x.rgb[1] === this.rgb[1] && x.rgb[2] === this.rgb[2] && - x.alpha === this.alpha) ? 0 : -1; + x.alpha === this.alpha) ? 0 : undefined; } }; diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 56d221efb..c9ef56c62 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -14,34 +14,20 @@ tree.Condition.prototype = { this.rvalue = visitor.visit(this.rvalue); }, eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { + var result = (function (op, a, b) { switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; + case 'and': return a && b; + case 'or': return a || b; default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { + switch (tree.compare(a, b)) { case -1: return op === '<' || op === '=<' || op === '<='; case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; case 1: return op === '>' || op === '>='; + default: return false; } } - })(this.op); + }) (this.op, this.lvalue.eval(env), this.rvalue.eval(env)); + return this.negate ? !result : result; } }; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index aecec136b..aabf3374e 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -88,33 +88,24 @@ tree.Dimension.prototype = { }, compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } + var a, b; + + if (!(other instanceof tree.Dimension)) { + return undefined; + } + + if (this.unit.isEmpty() || other.unit.isEmpty()) { + a = this; + b = other; } else { - return -1; + a = this.unify(); + b = other.unify(); + if (a.unit.compare(b.unit) !== 0) { + return undefined; + } } + + return tree.numericCompare(a.value, b.value); }, unify: function () { @@ -220,7 +211,7 @@ tree.Unit.prototype = { }, compare: function (other) { - return this.is(other.toString()) ? 0 : -1; + return this.is(other.toString()) ? 0 : undefined; }, is: function (unitString) { diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 830594d88..4b633b70f 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -8,14 +8,7 @@ tree.Keyword.prototype = { if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } output.add(this.value); }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } + toCSS: tree.toCSS }; tree.True = new(tree.Keyword)('true'); diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index b8f63b9ce..d20939e12 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -29,27 +29,13 @@ tree.Quoted.prototype = { }); return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - + compare: function (other) { // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; + if (other.type === "Quoted" && !this.escaped && !other.escaped) { + return tree.numericCompare(this.value, other.value); } else { - left = this.toCSS(); - right = x.toCSS(); + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; } }; diff --git a/test/css/mixins-guards.css b/test/css/mixins-guards.css index 59b6f932c..c10760b96 100644 --- a/test/css/mixins-guards.css +++ b/test/css/mixins-guards.css @@ -79,6 +79,80 @@ content: theme1 is not 'theme2'; content: theme1 is theme1; } +.variouse-types-comparision { + /**/ + content: true is not equal to false; + content: false is not equal to true too; + /**/ + content: 1 is not equal to true; + content: true is not equal to 1 too; + /**/ + content: 2 is equal to 2px; + content: 2px is equal to 2 too; + /**/ + content: 3 is equal to 3; + content: 3 is equal to 3 too; + /**/ + content: 5 is not equal to 4; + content: 4 is not equal to 5 too; + /**/ + content: abc is equal to abc; + content: abc is equal to abc too; + /**/ + content: abc is not equal to "abc"; + content: "abc" is not equal to abc too; + /**/ + content: 'abc' is less than "abd"; + content: "abd" is greater than 'abc' too; + content: 'abc' is not equal to "abd"; + content: "abd" is not equal to 'abc' too; + /**/ + content: 6 is equal to 6; + content: 6 is equal to 6 too; + /**/ + content: 8 is less than 9 too; + content: 9 is greater than 8; + content: 9 is not equal to 8; + content: 8 is not equal to 9 too; + /**/ + content: a is not equal to b; + content: b is not equal to a too; + /**/ + content: 1 2 is not equal to 3; + content: 3 is not equal to 1 2 too; +} +.list-comparision { + /**/ + content: a b c is equal to a b c; + content: a b c is equal to a b c too; + /**/ + content: a b c is not equal to a b d; + content: a b d is not equal to a b c too; + /**/ + content: a, b, c is equal to a, b, c; + content: a, b, c is equal to a, b, c too; + /**/ + content: a, b, c is not equal to a, b, d; + content: a, b, d is not equal to a, b, c too; + /**/ + content: 1 2px 300ms is equal to 1em 2 0.3s; + content: 1em 2 0.3s is equal to 1 2px 300ms too; + /**/ + content: 1 2 3 is not equal to 1, 2, 3; + content: 1, 2, 3 is not equal to 1 2 3 too; + /**/ + content: 1, 2, 3 is equal to 1, 2, 3; + content: 1, 2, 3 is equal to 1, 2, 3 too; + /**/ + content: 1 2 3 1, 2, 3 is equal to 1 2 3 1, 2, 3; + content: 1 2 3 1, 2, 3 is equal to 1 2 3 1, 2, 3 too; + /**/ + content: 1 2 3 1, 2, 3 is not equal to 1, 2, 3 1 2 3; + content: 1, 2, 3 1 2 3 is not equal to 1 2 3 1, 2, 3 too; + /**/ + content: 1 2 3 1, 2, 3 4 is equal to 1 2 3 1, 2, 3 4; + content: 1 2 3 1, 2, 3 4 is equal to 1 2 3 1, 2, 3 4 too; +} #tryNumberPx { catch: all; declare: 4; diff --git a/test/less/mixins-guards.less b/test/less/mixins-guards.less index 5aec2e51c..8d1ad0e69 100644 --- a/test/less/mixins-guards.less +++ b/test/less/mixins-guards.less @@ -130,6 +130,49 @@ .stringguard(theme1); } +.generic(@a, @b) {/**/} +.generic(@a, @b) when (@a = @b) {content: @a is equal to @b} +.generic(@a, @b) when (@b = @a) {content: @b is equal to @a too} +.generic(@a, @b) when (@a < @b) {content: @a is less than @b} +.generic(@a, @b) when (@b < @a) {content: @b is less than @a too} +.generic(@a, @b) when (@a > @b) {content: @a is greater than @b} +.generic(@a, @b) when (@b > @a) {content: @b is greater than @a too} +.generic(@a, @b) when not(@a = @b) {content: @a is not equal to @b} +.generic(@a, @b) when not(@b = @a) {content: @b is not equal to @a too} + +.variouse-types-comparision { + .generic(true, false); + .generic(1, true); + .generic(2, 2px); + .generic(3, ~"3"); + .generic(5, ~"4"); + .generic(abc, ~"abc"); + .generic(abc, "abc"); + .generic('abc', "abd"); + .generic(6, e("6")); + .generic(`9`, 8); + .generic(a, b); + .generic(1 2, 3); +} + +.list-comparision { + .generic(a b c, a b c); + .generic(a b c, a b d); + .generic(a, b, c; a, b, c); + .generic(a, b, c; a, b, d); + .generic(1 2px 300ms, 1em 2 .3s); + + @space-list: 1 2 3; + @comma-list: 1, 2, 3; + @compound: @space-list @comma-list; + + .generic(@space-list, @comma-list); + .generic(@comma-list, ~"1, 2, 3"); + .generic(@compound, @space-list @comma-list); + .generic(@compound, @comma-list @space-list); + .generic(@compound 4, ~"1 2 3 1, 2, 3 4"); +} + .mixin(...) { catch:all; } From 20025f10b1079acedc75d3b28d43ab093e6ae985 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 15:57:20 +0100 Subject: [PATCH 067/161] Remove duplication in render function --- lib/less/render.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/less/render.js b/lib/less/render.js index d4ea7cc5f..4893f9467 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,6 +1,6 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; -module.exports = function(Parser) { +var render = function(Parser) { return function (input, options, callback) { options = options || {}; @@ -9,19 +9,17 @@ module.exports = function(Parser) { options = {}; } - var parser = new(Parser)(options); - if (callback) { - parser.parse(input, function (e, root) { - if (e) { callback(e); return; } - var css; - try { - css = root && root.toCSS && root.toCSS(options); - } - catch (err) { callback(err); return; } - callback(null, css); - }, options); + render(input.options) + .then(function(css) { + callback(null, css); + }, + function(error) { + callback(error); + }); } else { + var parser = new(Parser)(options); + return new PromiseConstructor(function (resolve, reject) { parser.parse(input, function (e, root) { if (e) { return reject(e); } @@ -32,3 +30,4 @@ module.exports = function(Parser) { } }; }; +module.exports = render; From d3c6f2208fcb414a812a0102df252a80d8e2d22a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 17:42:38 +0100 Subject: [PATCH 068/161] Move toCSS out to a new class (still need to remove creation of that class from parser) --- lib/less/less-error.js | 44 +++++++++++-------- lib/less/parse-tree.js | 55 +++++++++++++++++++++++ lib/less/parser/imports.js | 2 +- lib/less/parser/parser-input.js | 21 --------- lib/less/parser/parser.js | 78 +++++---------------------------- lib/less/tree/javascript.js | 3 +- lib/less/tree/js-eval-node.js | 17 ++++--- lib/less/tree/variable.js | 4 +- lib/less/utils.js | 20 +++++++++ 9 files changed, 127 insertions(+), 117 deletions(-) create mode 100644 lib/less/parse-tree.js create mode 100644 lib/less/utils.js diff --git a/lib/less/less-error.js b/lib/less/less-error.js index 750bad806..e21bd5712 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -1,26 +1,32 @@ +var utils = require("./utils.js"); -var LessError = module.exports = function LessError(parser, e, env) { - var input = parser.getInput(e, env), - loc = parser.getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && parser.getLocation(e.call, input).line, - lines = input.split('\n'); +var LessError = module.exports = function LessError(e, importManager, currentFilename) { - this.type = e.type || 'Syntax'; + var filename = e.filename || currentFilename; + + if (importManager && filename) { + var input = importManager.contents[filename], + loc = utils.getLocation(e.index, input), + line = loc.line, + col = loc.column, + callLine = e.call && utils.getLocation(e.call, input).line, + lines = input.split('\n'); + + this.type = e.type || 'Syntax'; + this.filename = filename; + this.index = e.index; + this.line = typeof(line) === 'number' ? line + 1 : null; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; + this.column = col; + this.extract = [ + lines[line - 1], + lines[line], + lines[line + 1] + ]; + } this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; }; LessError.prototype = new Error(); diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js new file mode 100644 index 000000000..50f5f3b32 --- /dev/null +++ b/lib/less/parse-tree.js @@ -0,0 +1,55 @@ +var LessError = require('./less-error.js'), + transformTree = require("./transform-tree.js"); + +module.exports = function(environment) +{ +var SourceMapOutput = require("./source-map-output")(environment); + +var ParseTree = function(root, imports) { + this.root = root; + this.imports = imports; +}; + +ParseTree.prototype.toCSS = function(options) { + var evaldRoot; + try { + evaldRoot = transformTree(this.root, options); + } catch (e) { + throw new LessError(e, this.imports); + } + var css; + try { + if (options.sourceMap) { + evaldRoot = new SourceMapOutput( + { + contentsIgnoredCharsMap: this.imports.contentsIgnoredChars, + writeSourceMap: options.writeSourceMap, + rootNode: evaldRoot, + contentsMap: this.imports.contents, + sourceMapFilename: options.sourceMapFilename, + sourceMapURL: options.sourceMapURL, + outputFilename: options.sourceMapOutputFilename, + sourceMapBasepath: options.sourceMapBasepath, + sourceMapRootpath: options.sourceMapRootpath, + outputSourceFiles: options.outputSourceFiles, + sourceMapGenerator: options.sourceMapGenerator + }); + } + + css = evaldRoot.toCSS({ + compress: Boolean(options.compress), + dumpLineNumbers: options.dumpLineNumbers, + strictUnits: Boolean(options.strictUnits), + numPrecision: 8}); + } catch (e) { + throw new LessError(e, this.imports); + } + + if (options.compress) { + return css.replace(/(^(\s)+)|((\s)+$)/g, ""); + } else { + return css; + } +}; +return ParseTree; +}; diff --git a/lib/less/parser/imports.js b/lib/less/parser/imports.js index 2b7c4efad..7b2fd5642 100644 --- a/lib/less/parser/imports.js +++ b/lib/less/parser/imports.js @@ -70,7 +70,7 @@ module.exports = function(environment, env, Parser) { } else { new(Parser)(newEnv).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); - }); + }, null, true); } }); } diff --git a/lib/less/parser/parser-input.js b/lib/less/parser/parser-input.js index 7b03e249b..aca272f10 100644 --- a/lib/less/parser/parser-input.js +++ b/lib/less/parser/parser-input.js @@ -211,27 +211,6 @@ module.exports = function() { return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; }; - parserInput.getLocation = function(index, inputStream) { - inputStream = inputStream == null ? input : inputStream; - - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - }; - parserInput.start = function(str, chunkInput, parser, env) { input = str; parserInput.i = j = currentPos = furthest = 0; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 6d19dd9cf..aadef58a2 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -3,10 +3,11 @@ var LessError = require('../less-error.js'), visitor = require("../visitor/index.js"), contexts = require("../contexts.js"), getImportManager = require("./imports.js"), - getParserInput = require("./parser-input.js"); + getParserInput = require("./parser-input.js"), + utils = require("../utils.js"); module.exports = function(environment) { -var SourceMapOutput = require("../source-map-output")(environment); + var ParseTree = require("../parse-tree.js")(environment); // // less.js - parser // @@ -79,20 +80,12 @@ var Parser = function Parser(env) { throw e; } - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return parserInput.getInput(); - } - } - function getDebugInfo(index) { var filename = env.currentFileInfo.filename; filename = environment.getAbsolutePath(env, filename); return { - lineNumber: parserInput.getLocation(index).line + 1, + lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, fileName: filename }; } @@ -109,7 +102,7 @@ var Parser = function Parser(env) { // @param callback call `callback` when done. // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply // - parse: function (str, callback, additionalData) { + parse: function (str, callback, additionalData, returnSubParseTree) { var root, error = null, globalVars, modifyVars, preText = ""; globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : ''; @@ -136,54 +129,9 @@ var Parser = function Parser(env) { root.root = true; root.firstRoot = true; } catch (e) { - return callback(new LessError(parser, e, env)); + return callback(new LessError(e, parser.imports, env.currentFileInfo.filename)); } - root.toCSS = (function (evaluate) { - return function (options) { - var transformTree = require("../transform-tree.js"), - evaldRoot; - try { - evaldRoot = transformTree(this, options); - } catch (e) { - throw new LessError(parser, e, env); - } - var css; - try { - if (options.sourceMap) { - evaldRoot = new SourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new LessError(parser, e, env); - } - - if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - // If `i` is smaller than the `input.length - 1`, // it means the parser wasn't able to parse the whole // string, so we've got a parsing error. @@ -208,12 +156,12 @@ var Parser = function Parser(env) { } } - error = new LessError(parser, { + error = new LessError({ type: "Parse", message: message, index: endInfo.furthest, filename: env.currentFileInfo.filename - }, env); + }, parser.imports); } var finish = function (e) { @@ -221,13 +169,14 @@ var Parser = function Parser(env) { if (e) { if (!(e instanceof LessError)) { - e = new LessError(parser, e, env); + e = new LessError(e, parser.imports, env.currentFileInfo.filename); } return callback(e); } else { - return callback(null, root); + return callback(null, + returnSubParseTree ? root : new ParseTree(root, parser.imports)); } }; @@ -547,7 +496,7 @@ var Parser = function Parser(env) { js = parserInput.$re(/^(~)?`([^`]*)`/); if (js) { - return new(tree.JavaScript)(js[2], index, Boolean(js[1])); + return new(tree.JavaScript)(js[2], Boolean(js[1]), index, env.currentFileInfo); } } }, @@ -1636,9 +1585,6 @@ var Parser = function Parser(env) { } }; - parser.getInput = getInput; - parser.getLocation = parserInput.getLocation; - return parser; }; Parser.serializeVars = function(vars) { diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index a8f5533c8..4b5a2c07d 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -3,10 +3,11 @@ var JsEvalNode = require("./js-eval-node.js"), Quoted = require("./quoted.js"), Anonymous = require("./anonymous.js"); -var JavaScript = function (string, index, escaped) { +var JavaScript = function (string, escaped, index, currentFileInfo) { this.escaped = escaped; this.expression = string; this.index = index; + this.currentFileInfo = currentFileInfo; }; JavaScript.prototype = new JsEvalNode(); JavaScript.prototype.type = "JavaScript"; diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js index 35cabab1f..297a7a191 100644 --- a/lib/less/tree/js-eval-node.js +++ b/lib/less/tree/js-eval-node.js @@ -1,28 +1,30 @@ var Node = require("./node.js"), Variable = require("./variable.js"); -var jsEvalNode = function() { +var JsEvalNode = function() { }; -jsEvalNode.prototype = new Node(); +JsEvalNode.prototype = new Node(); -jsEvalNode.prototype.evaluateJavaScript = function (expression, env) { +JsEvalNode.prototype.evaluateJavaScript = function (expression, env) { var result, that = this, context = {}; if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - throw { message: "You are using JavaScript, which has been disabled." , + throw { message: "You are using JavaScript, which has been disabled.", + filename: this.currentFileInfo.filename, index: this.index }; } expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return that.jsify(new(Variable)('@' + name, that.index).eval(env)); + return that.jsify(new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env)); }); try { expression = new(Function)('return (' + expression + ')'); } catch (e) { throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , + filename: this.currentFileInfo.filename, index: this.index }; } @@ -43,11 +45,12 @@ jsEvalNode.prototype.evaluateJavaScript = function (expression, env) { result = expression.call(context); } catch (e) { throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , + filename: this.currentFileInfo.filename, index: this.index }; } return result; }; -jsEvalNode.prototype.jsify = function (obj) { +JsEvalNode.prototype.jsify = function (obj) { if (Array.isArray(obj.value) && (obj.value.length > 1)) { return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; } else { @@ -55,4 +58,4 @@ jsEvalNode.prototype.jsify = function (obj) { } }; -module.exports = jsEvalNode; +module.exports = JsEvalNode; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 85641a752..cff740ef4 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -11,13 +11,13 @@ Variable.prototype.eval = function (env) { var variable, name = this.name; if (name.indexOf('@@') === 0) { - name = '@' + new(Variable)(name.slice(1)).eval(env).value; + name = '@' + new(Variable)(name.slice(1), this.index, this.currentFileInfo).eval(env).value; } if (this.evaluating) { throw { type: 'Name', message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, + filename: this.currentFileInfo.filename, index: this.index }; } diff --git a/lib/less/utils.js b/lib/less/utils.js new file mode 100644 index 000000000..6efdb21bb --- /dev/null +++ b/lib/less/utils.js @@ -0,0 +1,20 @@ +module.exports = { + getLocation: function(index, inputStream) { + var n = index + 1, + line = null, + column = -1; + + while (--n >= 0 && inputStream.charAt(n) !== '\n') { + column++; + } + + if (typeof index === 'number') { + line = (inputStream.slice(0, index).match(/\n/g) || "").length; + } + + return { + line: line, + column: column + }; + } +}; From 9af3819822ecd70af2b311c866b46532a6dd03c0 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 17:56:12 +0100 Subject: [PATCH 069/161] parser no longer needs to expose imports (they are on the parseTree) and can use a variable instead of a reference to itself --- lib/less/parser/imports.js | 2 +- lib/less/parser/parser.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/less/parser/imports.js b/lib/less/parser/imports.js index 7b2fd5642..b45e533ed 100644 --- a/lib/less/parser/imports.js +++ b/lib/less/parser/imports.js @@ -3,7 +3,7 @@ module.exports = function(environment, env, Parser) { var rootFilename = env && env.filename; return { paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet + queue: [], // Files which haven't been imported yet files: env.files, // Holds the imported parse trees contents: env.contents, // Holds the imported file contents contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index aadef58a2..efbe0f7a5 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -53,7 +53,7 @@ var Parser = function Parser(env) { } this.env = env; - var imports = this.imports = getImportManager(environment, env, Parser); + var imports = getImportManager(environment, env, Parser); function expect(arg, msg, index) { // some older browsers return typeof 'function' for RegExp @@ -110,13 +110,13 @@ var Parser = function Parser(env) { if (globalVars || (additionalData && additionalData.banner)) { preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; + imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; } str = str.replace(/\r\n/g, '\n'); // Remove potential UTF Byte Order Mark str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; + imports.contents[env.currentFileInfo.filename] = str; // Start with the primary rule. // The whole syntax tree is held under a Ruleset node, @@ -129,7 +129,7 @@ var Parser = function Parser(env) { root.root = true; root.firstRoot = true; } catch (e) { - return callback(new LessError(e, parser.imports, env.currentFileInfo.filename)); + return callback(new LessError(e, imports, env.currentFileInfo.filename)); } // If `i` is smaller than the `input.length - 1`, @@ -161,27 +161,27 @@ var Parser = function Parser(env) { message: message, index: endInfo.furthest, filename: env.currentFileInfo.filename - }, parser.imports); + }, imports); } var finish = function (e) { - e = error || e || parser.imports.error; + e = error || e || imports.error; if (e) { if (!(e instanceof LessError)) { - e = new LessError(e, parser.imports, env.currentFileInfo.filename); + e = new LessError(e, imports, env.currentFileInfo.filename); } return callback(e); } else { return callback(null, - returnSubParseTree ? root : new ParseTree(root, parser.imports)); + returnSubParseTree ? root : new ParseTree(root, imports)); } }; if (env.processImports !== false) { - new visitor.ImportVisitor(this.imports, finish) + new visitor.ImportVisitor(imports, finish) .run(root); } else { return finish(); From ca77dc3a6e2922571592d981b5eeecc7a837be50 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 17:58:12 +0100 Subject: [PATCH 070/161] Parser does not need to have a parseEnv stored on itself --- lib/less/parser/parser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index efbe0f7a5..b5e474bec 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -51,7 +51,6 @@ var Parser = function Parser(env) { if (!(env instanceof contexts.parseEnv)) { env = new contexts.parseEnv(env); } - this.env = env; var imports = getImportManager(environment, env, Parser); From e5faba5154da3e92159e3c57508a65fc2d4a08c4 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 18:16:08 +0100 Subject: [PATCH 071/161] Remove some interdepedencies --- lib/less/parser/parser-input.js | 6 +++--- lib/less/parser/parser.js | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/less/parser/parser-input.js b/lib/less/parser/parser-input.js index aca272f10..5c799c624 100644 --- a/lib/less/parser/parser-input.js +++ b/lib/less/parser/parser-input.js @@ -211,7 +211,7 @@ module.exports = function() { return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; }; - parserInput.start = function(str, chunkInput, parser, env) { + parserInput.start = function(str, chunkInput, env) { input = str; parserInput.i = j = currentPos = furthest = 0; @@ -227,12 +227,12 @@ module.exports = function() { // in the case it causes too much performance issues. if (chunkInput) { chunks = chunker(str, function fail(msg, index) { - throw new(LessError)(parser, { + throw new(LessError)({ index: index, type: 'Parse', message: msg, filename: env.currentFileInfo.filename - }, env); + }, env); //TODO works because env contains contents like importManager }); } else { chunks = [str]; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index b5e474bec..70027d0d8 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -42,8 +42,7 @@ module.exports = function(environment) { // // var Parser = function Parser(env) { - var parser, - parsers, + var parsers, parserInput = getParserInput(); // Top parser on an import tree must be sure there is one "env" @@ -73,10 +72,15 @@ var Parser = function Parser(env) { } function error(msg, type) { - var e = new Error(msg); - e.index = parserInput.i; - e.type = type || 'Syntax'; - throw e; + throw new LessError( + { + index: parserInput.i, + filename: env.currentFileInfo.filename, + type: type || 'Syntax', + message: msg + }, + imports + ); } function getDebugInfo(index) { @@ -92,9 +96,8 @@ var Parser = function Parser(env) { // // The Parser // - parser = { + return { - imports: imports, // // Parse an input string into an abstract syntax tree, // @param str A string containing 'less' markup @@ -122,7 +125,7 @@ var Parser = function Parser(env) { // with the `root` property set to true, so no `{}` are // output. The callback is called when the input is parsed. try { - parserInput.start(str, env.chunkInput, parser, env); + parserInput.start(str, env.chunkInput, env); root = new(tree.Ruleset)(null, this.parsers.primary()); root.root = true; @@ -618,7 +621,7 @@ var Parser = function Parser(env) { parserInput.restore(); }, args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, + var entities = parsers.entities, returner = { args:null, variadic: false }, expressions = [], argsSemiColon = [], argsComma = [], isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; @@ -1583,8 +1586,6 @@ var Parser = function Parser(env) { } } }; - - return parser; }; Parser.serializeVars = function(vars) { var s = ''; From 4f5a449b0b654fb75c3fedd5de7e0bac65f4b5e8 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 18:24:24 +0100 Subject: [PATCH 072/161] move creation of env to invoker --- lib/less/parser/parser.js | 7 ------- lib/less/render.js | 7 +++++-- test/less-test.js | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 70027d0d8..5c323abf1 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -1,7 +1,6 @@ var LessError = require('../less-error.js'), tree = require("../tree/index.js"), visitor = require("../visitor/index.js"), - contexts = require("../contexts.js"), getImportManager = require("./imports.js"), getParserInput = require("./parser-input.js"), utils = require("../utils.js"); @@ -45,12 +44,6 @@ var Parser = function Parser(env) { var parsers, parserInput = getParserInput(); - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof contexts.parseEnv)) { - env = new contexts.parseEnv(env); - } - var imports = getImportManager(environment, env, Parser); function expect(arg, msg, index) { diff --git a/lib/less/render.js b/lib/less/render.js index 4893f9467..df31b379a 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,4 +1,5 @@ -var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, + contexts = require("./contexts.js"); var render = function(Parser) { return function (input, options, callback) { @@ -18,7 +19,9 @@ var render = function(Parser) { callback(error); }); } else { - var parser = new(Parser)(options); + var env = new contexts.parseEnv(options); + + var parser = new(Parser)(env); return new PromiseConstructor(function (resolve, reject) { parser.parse(input, function (e, root) { diff --git a/test/less-test.js b/test/less-test.js index de13db7df..9d2a0dbfa 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -231,7 +231,7 @@ module.exports = function() { totalTests++; try { process.stdout.write("- Integration - creating parser without options: "); - new(less.Parser)(); + less.render(""); } catch(e) { fail(stylize("FAIL\n", "red")); return; From a46de448a3aebabb7b871febd0c4ac9566e61d9a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 18:35:56 +0100 Subject: [PATCH 073/161] move import manager creation out of parser --- lib/less/{parser => }/imports.js | 8 ++++---- lib/less/non-node-index.js | 2 +- lib/less/parser/parser.js | 11 +++-------- lib/less/render.js | 16 +++++++++++----- 4 files changed, 19 insertions(+), 18 deletions(-) rename lib/less/{parser => }/imports.js (92%) diff --git a/lib/less/parser/imports.js b/lib/less/imports.js similarity index 92% rename from lib/less/parser/imports.js rename to lib/less/imports.js index b45e533ed..e31a37573 100644 --- a/lib/less/parser/imports.js +++ b/lib/less/imports.js @@ -1,5 +1,5 @@ -var contexts = require("../contexts.js"); -module.exports = function(environment, env, Parser) { +var contexts = require("./contexts.js"); +var getImportManager = module.exports = function(environment, env, Parser) { var rootFilename = env && env.filename; return { paths: env.paths || [], // Search paths, when importing @@ -68,9 +68,9 @@ module.exports = function(environment, env, Parser) { if (importOptions.inline) { fileParsedFunc(null, contents, resolvedFilename); } else { - new(Parser)(newEnv).parse(contents, function (e, root) { + new(Parser)(newEnv, getImportManager(environment, env, Parser)).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); - }, null, true); + }); } }); } diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 0e43bc0b0..7abfc1b7e 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -9,7 +9,7 @@ module.exports = function(environment) { contexts: require("./contexts.js"), environment: environment }; - less.render = require("./render.js")(less.Parser); + less.render = require("./render.js")(less.Parser, environment); return less; }; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 5c323abf1..c2c2ed16a 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -1,12 +1,10 @@ var LessError = require('../less-error.js'), tree = require("../tree/index.js"), visitor = require("../visitor/index.js"), - getImportManager = require("./imports.js"), getParserInput = require("./parser-input.js"), utils = require("../utils.js"); module.exports = function(environment) { - var ParseTree = require("../parse-tree.js")(environment); // // less.js - parser // @@ -40,12 +38,10 @@ module.exports = function(environment) { // It also takes care of moving all the indices forwards. // // -var Parser = function Parser(env) { +var Parser = function Parser(env, imports) { var parsers, parserInput = getParserInput(); - var imports = getImportManager(environment, env, Parser); - function expect(arg, msg, index) { // some older browsers return typeof 'function' for RegExp var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg); @@ -97,7 +93,7 @@ var Parser = function Parser(env) { // @param callback call `callback` when done. // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply // - parse: function (str, callback, additionalData, returnSubParseTree) { + parse: function (str, callback, additionalData) { var root, error = null, globalVars, modifyVars, preText = ""; globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : ''; @@ -170,8 +166,7 @@ var Parser = function Parser(env) { return callback(e); } else { - return callback(null, - returnSubParseTree ? root : new ParseTree(root, imports)); + return callback(null, root); } }; diff --git a/lib/less/render.js b/lib/less/render.js index df31b379a..fcce38241 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,7 +1,9 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, - contexts = require("./contexts.js"); + contexts = require("./contexts.js"), + getImportManager = require("./imports.js"); -var render = function(Parser) { +var render = function(Parser, environment) { + var ParseTree = require("./parse-tree.js")(environment); return function (input, options, callback) { options = options || {}; @@ -19,14 +21,18 @@ var render = function(Parser) { callback(error); }); } else { - var env = new contexts.parseEnv(options); + var env = new contexts.parseEnv(options), + imports = getImportManager(environment, env, Parser); - var parser = new(Parser)(env); + var parser = new(Parser)(env, imports); return new PromiseConstructor(function (resolve, reject) { parser.parse(input, function (e, root) { if (e) { return reject(e); } - try { resolve(root.toCSS(options)); } + try { + var parseTree = new ParseTree(root, imports); + resolve(parseTree.toCSS(options)); + } catch (err) { reject( err); } }, options); }); From 9d230623d2d330ffc434307948e403784cfa0835 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 18:42:00 +0100 Subject: [PATCH 074/161] Parser no longer requires environment --- lib/less/imports.js | 13 ++++++++++--- lib/less/non-node-index.js | 4 ++-- lib/less/parser/parser.js | 6 ++---- lib/less/render.js | 7 ++++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/less/imports.js b/lib/less/imports.js index e31a37573..d0e149d13 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -1,5 +1,7 @@ -var contexts = require("./contexts.js"); -var getImportManager = module.exports = function(environment, env, Parser) { +var contexts = require("./contexts.js"), + Parser = require('./parser/parser.js'); + +var getImportManager = module.exports = function(environment, env) { var rootFilename = env && env.filename; return { paths: env.paths || [], // Search paths, when importing @@ -9,6 +11,11 @@ var getImportManager = module.exports = function(environment, env, Parser) { contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less mime: env.mime, // MIME type of .less files error: null, // Error in parsing/evaluating an import + getAbsolutePath: function(env, filename) { + // proxy needed for "DebugInfo" + // I hope one day we can remove this function + return environment.getAbsolutePath(env, filename); + }, push: function (path, currentFileInfo, importOptions, callback) { var parserImports = this; this.queue.push(path); @@ -68,7 +75,7 @@ var getImportManager = module.exports = function(environment, env, Parser) { if (importOptions.inline) { fileParsedFunc(null, contents, resolvedFilename); } else { - new(Parser)(newEnv, getImportManager(environment, env, Parser)).parse(contents, function (e, root) { + new(Parser)(newEnv, getImportManager(environment, env)).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); }); } diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js index 7abfc1b7e..2b2ed2bed 100644 --- a/lib/less/non-node-index.js +++ b/lib/less/non-node-index.js @@ -4,12 +4,12 @@ module.exports = function(environment) { data: require('./data/index.js'), tree: require('./tree/index.js'), visitor: require('./visitor/index.js'), - Parser: require('./parser/parser.js')(environment), + Parser: require('./parser/parser.js'), functions: require('./functions/index.js')(environment), contexts: require("./contexts.js"), environment: environment }; - less.render = require("./render.js")(less.Parser, environment); + less.render = require("./render.js")(environment); return less; }; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index c2c2ed16a..9e62b9fce 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -4,7 +4,6 @@ var LessError = require('../less-error.js'), getParserInput = require("./parser-input.js"), utils = require("../utils.js"); -module.exports = function(environment) { // // less.js - parser // @@ -74,7 +73,7 @@ var Parser = function Parser(env, imports) { function getDebugInfo(index) { var filename = env.currentFileInfo.filename; - filename = environment.getAbsolutePath(env, filename); + filename = imports.getAbsolutePath(env, filename); return { lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, @@ -1589,5 +1588,4 @@ Parser.serializeVars = function(vars) { return s; }; -return Parser; -}; +module.exports = Parser; diff --git a/lib/less/render.js b/lib/less/render.js index fcce38241..83f909860 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,8 +1,9 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, contexts = require("./contexts.js"), - getImportManager = require("./imports.js"); + getImportManager = require("./imports.js"), + Parser = require('./parser/parser.js'); -var render = function(Parser, environment) { +var render = function(environment) { var ParseTree = require("./parse-tree.js")(environment); return function (input, options, callback) { options = options || {}; @@ -22,7 +23,7 @@ var render = function(Parser, environment) { }); } else { var env = new contexts.parseEnv(options), - imports = getImportManager(environment, env, Parser); + imports = getImportManager(environment, env); var parser = new(Parser)(env, imports); From d9c42e206d7c26e8c35a6b270678303ac83cdd78 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 4 Sep 2014 19:11:59 +0100 Subject: [PATCH 075/161] seperate out the environments at the top-most-level --- Gruntfile.js | 2 +- bin/lessc | 6 +- .../environment.js} | 0 .../browser.js => less-browser/index.js} | 4 +- .../node.js => less-node/environment.js} | 2 +- .../environment/node => less-node}/fs.js | 0 lib/less-node/index.js | 60 +++++++++++++++ .../lessc-helper.js} | 0 lib/less/environment/api.js | 6 +- lib/less/index.js | 73 ++++--------------- lib/less/non-node-index.js | 15 ---- test/index.js | 2 +- test/less-test.js | 4 +- 13 files changed, 87 insertions(+), 87 deletions(-) rename lib/{less/environment/browser.js => less-browser/environment.js} (100%) rename lib/{less/browser.js => less-browser/index.js} (99%) rename lib/{less/environment/node.js => less-node/environment.js} (99%) rename lib/{less/environment/node => less-node}/fs.js (100%) create mode 100644 lib/less-node/index.js rename lib/{less/lessc_helper.js => less-node/lessc-helper.js} (100%) delete mode 100644 lib/less/non-node-index.js diff --git a/Gruntfile.js b/Gruntfile.js index 9a70c8d42..126cb2366 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -49,7 +49,7 @@ module.exports = function (grunt) { browserify: { browser: { - src: ['./lib/less/browser.js'], + src: ['./lib/less-browser/index.js'], dest: 'tmp/less.js' } }, diff --git a/bin/lessc b/bin/lessc index 9edcd9584..d8e195366 100755 --- a/bin/lessc +++ b/bin/lessc @@ -1,11 +1,11 @@ #!/usr/bin/env node var path = require('path'), - fs = require('../lib/less/environment/node/fs'), + fs = require('../lib/less-node/fs'), os = require('os'), mkdirp; -var less = require('../lib/less'); +var less = require('../lib/less-node'); var args = process.argv.slice(1); var options = { depends: false, @@ -98,7 +98,7 @@ args = args.filter(function (arg) { break; case 'h': case 'help': - require('../lib/less/lessc_helper').printUsage(); + require('../lib/less-node/lessc-helper').printUsage(); continueProcessing = false; break; case 'x': diff --git a/lib/less/environment/browser.js b/lib/less-browser/environment.js similarity index 100% rename from lib/less/environment/browser.js rename to lib/less-browser/environment.js diff --git a/lib/less/browser.js b/lib/less-browser/index.js similarity index 99% rename from lib/less/browser.js rename to lib/less-browser/index.js index 5b9e9e0d7..7b7154838 100644 --- a/lib/less/browser.js +++ b/lib/less-browser/index.js @@ -29,9 +29,9 @@ function log(str, level) { var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), options = window.less, - environment = require("./environment/browser.js")(options, isFileProtocol, log, logLevel); + environment = require("./environment.js")(options, isFileProtocol, log, logLevel); -window.less = less = require('./non-node-index.js')(environment); +window.less = less = require('../less/index.js')(environment); less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || diff --git a/lib/less/environment/node.js b/lib/less-node/environment.js similarity index 99% rename from lib/less/environment/node.js rename to lib/less-node/environment.js index 9c2f8e980..3340091e2 100644 --- a/lib/less/environment/node.js +++ b/lib/less-node/environment.js @@ -1,7 +1,7 @@ var path = require('path'), url = require('url'), request, - fs = require('./node/fs'), + fs = require('./fs'), isUrlRe = /^(?:https?:)?\/\//i; module.exports = { diff --git a/lib/less/environment/node/fs.js b/lib/less-node/fs.js similarity index 100% rename from lib/less/environment/node/fs.js rename to lib/less-node/fs.js diff --git a/lib/less-node/index.js b/lib/less-node/index.js new file mode 100644 index 000000000..d624dbabe --- /dev/null +++ b/lib/less-node/index.js @@ -0,0 +1,60 @@ +var environment = require("./environment.js"); +var less = require("../less/index.js")(environment); + +less.formatError = function(ctx, options) { + options = options || {}; + + var message = ""; + var extract = ctx.extract; + var error = []; + var stylize = options.color ? require('./lessc-helper').stylize : function (str) { return str; }; + + // only output a stack if it isn't a less error + if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } + + if (!ctx.hasOwnProperty('index') || !extract) { + return ctx.stack || ctx.message; + } + + if (typeof(extract[0]) === 'string') { + error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); + } + + if (typeof(extract[1]) === 'string') { + var errorTxt = ctx.line + ' '; + if (extract[1]) { + errorTxt += extract[1].slice(0, ctx.column) + + stylize(stylize(stylize(extract[1].substr(ctx.column, 1), 'bold') + + extract[1].slice(ctx.column + 1), 'red'), 'inverse'); + } + error.push(errorTxt); + } + + if (typeof(extract[2]) === 'string') { + error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); + } + error = error.join('\n') + stylize('', 'reset') + '\n'; + + message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); + if (ctx.filename) { + message += stylize(' in ', 'red') + ctx.filename + + stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); + } + + message += '\n' + error; + + if (ctx.callLine) { + message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; + message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; + } + + return message; +}; + +less.writeError = function (ctx, options) { + options = options || {}; + if (options.silent) { return; } + console.error(less.formatError(ctx, options)); +}; + +module.exports = less; diff --git a/lib/less/lessc_helper.js b/lib/less-node/lessc-helper.js similarity index 100% rename from lib/less/lessc_helper.js rename to lib/less-node/lessc-helper.js diff --git a/lib/less/environment/api.js b/lib/less/environment/api.js index 74f0dcc0b..ef3dee294 100644 --- a/lib/less/environment/api.js +++ b/lib/less/environment/api.js @@ -9,7 +9,7 @@ module.exports = { /** * gets the path from the filename, e.g. "http://wwe.files.com/ha/ha.less" would return * "http://wwe.files.com/ha/" - * If the filename is a file e.g. "file.less" it should return the empty string "" + * If the filename is a file e.g. "file.less" it should return the empty string "" * @param {Object} env - the environment or options object * @param {String} filename - the filename to extract the path. * @returns {String} @@ -32,11 +32,11 @@ module.exports = { * @param {Function} callback - a function to callback when finished, * taking the format callback(error, contents, fullfilename, reserved) * where error is { type: {string}, message: {string} }, contents is {string} and fullfilename is {string} - * for reserved, see browser.js which uses this argument for cache information + * for reserved, see less-browser/index.js which uses this argument for cache information * @returns {Boolean} */ loadFile: function(env, filename, currentDirectory, callback) { }, supportsDataURI: function(env) { } -}; \ No newline at end of file +}; diff --git a/lib/less/index.js b/lib/less/index.js index cb4719f33..2b2ed2bed 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,60 +1,15 @@ -var environment = require("./environment/node"); -var less = require("./non-node-index.js")(environment); - -less.formatError = function(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1].substr(ctx.column, 1), 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; +module.exports = function(environment) { + var less = { + version: [2, 0, 0], + data: require('./data/index.js'), + tree: require('./tree/index.js'), + visitor: require('./visitor/index.js'), + Parser: require('./parser/parser.js'), + functions: require('./functions/index.js')(environment), + contexts: require("./contexts.js"), + environment: environment + }; + less.render = require("./render.js")(environment); + + return less; }; - -less.writeError = function (ctx, options) { - options = options || {}; - if (options.silent) { return; } - console.error(less.formatError(ctx, options)); -}; - -module.exports = less; diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js deleted file mode 100644 index 2b2ed2bed..000000000 --- a/lib/less/non-node-index.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = function(environment) { - var less = { - version: [2, 0, 0], - data: require('./data/index.js'), - tree: require('./tree/index.js'), - visitor: require('./visitor/index.js'), - Parser: require('./parser/parser.js'), - functions: require('./functions/index.js')(environment), - contexts: require("./contexts.js"), - environment: environment - }; - less.render = require("./render.js")(environment); - - return less; -}; diff --git a/test/index.js b/test/index.js index ab281101c..600df98dd 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,7 @@ var lessTest = require("./less-test"), lessTester = lessTest(), path = require("path"), - stylize = require('../lib/less/lessc_helper').stylize; + stylize = require('../lib/less-node/lessc-helper').stylize; function getErrorPathReplacementFunction(dir) { return function(input) { diff --git a/test/less-test.js b/test/less-test.js index 9d2a0dbfa..2da185c06 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -4,8 +4,8 @@ module.exports = function() { var path = require('path'), fs = require('fs'); - var less = require('../lib/less'); - var stylize = require('../lib/less/lessc_helper').stylize; + var less = require('../lib/less-node'); + var stylize = require('../lib/less-node/lessc-helper').stylize; var globals = Object.keys(global); From c330c3a4a8b14a525ae2bd57a00ea1e1e305cb2d Mon Sep 17 00:00:00 2001 From: jurcovicovam Date: Fri, 5 Sep 2014 09:01:56 +0200 Subject: [PATCH 076/161] Added guards checking to namespaces. Issue #1418. --- lib/less/tree/mixin.js | 67 ++++++++++++++++++++++-------------- lib/less/tree/ruleset.js | 15 +++++--- test/css/mixins-guards.css | 8 +++++ test/less/mixins-guards.less | 55 +++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 30 deletions(-) diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index 94f48efa7..078f30e7e 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -19,16 +19,45 @@ tree.mixin.Call.prototype = { } }, eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; + var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, + candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, + defaultResult, defFalseEitherCase=-1, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter; + + function calcDefGroup(mixin, mixinPath) { + var p, namespace; + + for (f = 0; f < 2; f++) { + conditionResult[f] = true; + defaultFunc.value(f); + for(p = 0; p < mixinPath.length && conditionResult[f]; p++) { + namespace = mixinPath[p]; + if (namespace.matchCondition) { + conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, env); + } + } + if (mixin.matchCondition) { + conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, env); + } + } + if (conditionResult[0] || conditionResult[1]) { + if (conditionResult[0] != conditionResult[1]) { + return conditionResult[1] ? + defTrue : defFalse; + } + + return defNone; + } + return defFalseEitherCase; + } args = this.arguments && this.arguments.map(function (a) { return { name: a.name, value: a.value.eval(env) }; }); + noArgumentsFilter = function(rule) {return rule.matchArgs(null, env);}; + for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { + if ((mixins = env.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) { isOneFound = true; // To make `default()` function independent of definition order we have two "subpasses" here. @@ -37,7 +66,8 @@ tree.mixin.Call.prototype = { // we make a final decision. for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; + mixin = mixins[m].rule; + mixinPath = mixins[m].path; isRecursive = false; for(f = 0; f < env.frames.length; f++) { if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { @@ -48,28 +78,14 @@ tree.mixin.Call.prototype = { if (isRecursive) { continue; } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); + if (mixin.matchArgs(args, env)) { + candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)}; + + if (candidate.group!==defFalseEitherCase) { + candidates.push(candidate); } - + match = true; } } @@ -133,6 +149,7 @@ tree.mixin.Call.prototype = { message: this.selector.toCSS().trim() + " is undefined", index: this.index, filename: this.currentFileInfo.filename }; } + }, format: function (args) { return this.selector.toCSS().trim() + '(' + diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 17c0ad1ab..f5cdf1f55 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -238,9 +238,9 @@ tree.Ruleset.prototype = { var rules = this.rules; if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } }, - find: function (selector, self) { + find: function (selector, self, filter) { self = self || this; - var rules = [], match, + var rules = [], match, foundMixins, key = selector.toCSS(); if (key in this._lookups) { return this._lookups[key]; } @@ -251,10 +251,15 @@ tree.Ruleset.prototype = { match = selector.match(rule.selectors[j]); if (match) { if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); + if (!filter || filter(rule)) { + foundMixins = rule.find(new(tree.Selector)(selector.elements.slice(match)), self, filter); + for (var i = 0; i < foundMixins.length; ++i) { + foundMixins[i].path.push(rule); + } + Array.prototype.push.apply(rules, foundMixins); + } } else { - rules.push(rule); + rules.push({ rule: rule, path: []}); } break; } diff --git a/test/css/mixins-guards.css b/test/css/mixins-guards.css index 59b6f932c..b918cda6e 100644 --- a/test/css/mixins-guards.css +++ b/test/css/mixins-guards.css @@ -91,3 +91,11 @@ .mixin-generated-class { a: 1; } +#guarded-caller { + guarded: namespace; + silent: namespace; + guarded: with default; +} +#guarded-deeper { + should: match 1; +} diff --git a/test/less/mixins-guards.less b/test/less/mixins-guards.less index 5aec2e51c..33a35830a 100644 --- a/test/less/mixins-guards.less +++ b/test/less/mixins-guards.less @@ -171,3 +171,58 @@ } #ns > .mixin-for-root-usage(1); + +@namespaceGuard: 1; +#guarded when (@namespaceGuard>0) { + #deeper { + .mixin() { + guarded: namespace; + } + } +} +#guarded() when (@namespaceGuard>0) { + #deeper { + .mixin() { + silent: namespace; + } + } +} +#guarded(@variable) when (@namespaceGuard>0) { + #deeper { + .mixin() { + should: not match because namespace argument; + } + } +} +#guarded(@variable: default) when (@namespaceGuard>0) { + #deeper { + .mixin() { + guarded: with default; + } + } +} +#guarded when (@namespaceGuard<0) { + #deeper { + .mixin() { + should: not match because namespace guard; + } + } +} +#guarded-caller { + #guarded > #deeper > .mixin(); +} +#top { + #deeper when (@namespaceGuard<0) { + .mixin(@a) { + should: not match because namespace guard; + } + } + #deeper() when (@namespaceGuard>0) { + .mixin(@a) { + should: match @a; + } + } +} +#guarded-deeper { + #top > #deeper > .mixin(1); +} \ No newline at end of file From 9f68f4355282375866a74cc0e693a2abd40f8d5c Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Fri, 5 Sep 2014 14:11:03 +0400 Subject: [PATCH 077/161] Fix #1973 --- lib/less/tree/element.js | 13 +++++++++++-- test/css/selectors.css | 3 +++ test/less/selectors.less | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 8142d11f1..cc1c83ec0 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -1,4 +1,5 @@ var Node = require("./node.js"), + Paren = require("./paren.js"), Combinator = require("./combinator.js"); var Element = function (combinator, value, index, currentFileInfo) { @@ -34,11 +35,19 @@ Element.prototype.genCSS = function (env, output) { output.add(this.toCSS(env), this.currentFileInfo, this.index); }; Element.prototype.toCSS = function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); + env = env || {}; + var value = this.value, firstSelector = env.firstSelector; + if (value instanceof Paren) { + // selector in parens should not be affected by outer selector + // flags (breaks only interpolated selectors - see #1973) + env.firstSelector = true; + } + value = value.toCSS ? value.toCSS(env) : value; + env.firstSelector = firstSelector; if (value === '' && this.combinator.value.charAt(0) === '&') { return ''; } else { - return this.combinator.toCSS(env || {}) + value; + return this.combinator.toCSS(env) + value; } }; module.exports = Element; diff --git a/test/css/selectors.css b/test/css/selectors.css index 11909fe5f..85f95b56e 100644 --- a/test/css/selectors.css +++ b/test/css/selectors.css @@ -125,6 +125,9 @@ p a span { :nth-child(3) { selector: interpolated; } +.test:nth-child(3) { + selector: interpolated; +} .test:nth-child(odd):not(:nth-child(3)) { color: #ff0000; } diff --git a/test/less/selectors.less b/test/less/selectors.less index e6418473c..42aa29e72 100644 --- a/test/less/selectors.less +++ b/test/less/selectors.less @@ -126,6 +126,9 @@ a { selector: interpolated; } .test { + &:nth-child(@{num}) { + selector: interpolated; + } &:nth-child(odd):not(:nth-child(3)) { color: #ff0000; } From 796d37cc663a6f940b4e2cb9191628284f6f0bed Mon Sep 17 00:00:00 2001 From: jurcovicovam Date: Fri, 5 Sep 2014 16:47:37 +0200 Subject: [PATCH 078/161] Iterated string interpolation Solves issue #2094 . --- lib/less/tree/quoted.js | 23 +++++++++++++++-------- test/css/strings.css | 12 ++++++++++++ test/less/strings.less | 16 ++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index b8f63b9ce..da7bc6b7c 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -20,14 +20,21 @@ tree.Quoted.prototype = { }, toCSS: tree.toCSS, eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); + var that = this; + var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { + return new(tree.JavaScript)(exp, that.index, true).eval(env).value; + }); + var evaluatedValue = value; + var interpolationReplacementFnc = function (_, name) { + var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); + return (v instanceof tree.Quoted) ? v.value : v.toCSS(); + }; + do { + value = evaluatedValue; + evaluatedValue = value.replace(/@\{([\w-]+)\}/g, interpolationReplacementFnc); + } while (value!==evaluatedValue); + value = evaluatedValue; + return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); }, compare: function (x) { if (!x.toCSS) { diff --git a/test/css/strings.css b/test/css/strings.css index cd6d60202..92430862c 100644 --- a/test/css/strings.css +++ b/test/css/strings.css @@ -41,3 +41,15 @@ .watermark { family: Univers, Arial, Verdana, San-Serif; } +#iterated-interpolation .mixin { + width: 100px; + weird: 100px; + width-str: "100px"; + weird-str: "100px"; +} +#iterated-interpolation .interpolation-mixin { + width: 100px; + weird: 100px; + width-str: "100px"; + weird-str: "100px"; +} diff --git a/test/less/strings.less b/test/less/strings.less index c43e368dd..43746053d 100644 --- a/test/less/strings.less +++ b/test/less/strings.less @@ -55,3 +55,19 @@ @family: ~"Univers, @{test}"; family: @family; } +#iterated-interpolation { + @box-small: 10px; + @box-large: 100px; + + .mixin { // both ruleset and mixin + width: ~"@{box-@{suffix}}"; + weird: ~"@{box}-@{suffix}}"; + width-str: "@{box-@{suffix}}"; + weird-str: "@{box}-@{suffix}}"; + @box: ~"@{box"; + @suffix: large; + } + .interpolation-mixin { + .mixin(); //call the above as mixin + } +} From a038121676ac06a905324697a8fc5c41f30070fd Mon Sep 17 00:00:00 2001 From: jurcovicovam Date: Fri, 5 Sep 2014 17:04:06 +0200 Subject: [PATCH 079/161] Cleaned up and added iteration for javascript too. --- lib/less/tree/quoted.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index da7bc6b7c..6288d8010 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -20,20 +20,24 @@ tree.Quoted.prototype = { }, toCSS: tree.toCSS, eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { + var that = this, value = this.value; + var javascriptReplacement = function (_, exp) { return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }); - var evaluatedValue = value; - var interpolationReplacementFnc = function (_, name) { + }; + var interpolationReplacement = function (_, name) { var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); return (v instanceof tree.Quoted) ? v.value : v.toCSS(); }; - do { - value = evaluatedValue; - evaluatedValue = value.replace(/@\{([\w-]+)\}/g, interpolationReplacementFnc); - } while (value!==evaluatedValue); - value = evaluatedValue; + function iterativeReplace(value, regexp, replacementFnc) { + var evaluatedValue = value; + do { + value = evaluatedValue; + evaluatedValue = value.replace(regexp, replacementFnc); + } while (value!==evaluatedValue); + return evaluatedValue; + } + value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement); + value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement); return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); }, compare: function (x) { From 09360ea2140da3e8c679fc3e380663722e72b29b Mon Sep 17 00:00:00 2001 From: Luke Page Date: Fri, 5 Sep 2014 23:32:24 +0100 Subject: [PATCH 080/161] delete merged dist files --- dist/less-1.7.5.js | 8008 ------------------------------- dist/less-1.7.5.min.js | 16 - dist/less-rhino-1.7.5.js | 9383 ------------------------------------- dist/lessc-rhino-1.7.5.js | 449 -- 4 files changed, 17856 deletions(-) delete mode 100644 dist/less-1.7.5.js delete mode 100644 dist/less-1.7.5.min.js delete mode 100644 dist/less-rhino-1.7.5.js delete mode 100644 dist/lessc-rhino-1.7.5.js diff --git a/dist/less-1.7.5.js b/dist/less-1.7.5.js deleted file mode 100644 index e2ef80155..000000000 --- a/dist/less-1.7.5.js +++ /dev/null @@ -1,8008 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.5 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - - - -(function (window, undefined) {// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -}; - - -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg, index) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - if (!elements) - error("Missing target selector for :extend()."); - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '/') { - save(); - var slashedCombinator = $re(/^\/[a-z]+\//i); - if (slashedCombinator) { - forget(); - return new(tree.Combinator)(slashedCombinator); - } - restore(); - } - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (c === '^' && input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - this.comments(); - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(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. - // - "import": function () { - var path, features, index = i; - - var dir = $re(/^@import?\s+/); - - if (dir) { - var options = (dir ? this.importOptions() : null) || {}; - - if ((path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - - if (!$(';')) { - i = index; - error("missing semi-colon or unrecognised media features on import"); - } - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - else - { - i = index; - error("malformed import statement"); - } - } - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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; - } - - this.comments(); - - if (hasIdentifier) { - value = this.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - this.comments(); - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - - save(); - - op = $char('/') || $char('*'); - - if (!op) { forget(); break; } - - a = this.operand(); - - if (!a) { restore(); break; } - forget(); - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - function cutOutBlockComments() { - //match block comments - var a = /^\s*\/\*(?:[^*]|\*+[^\/*])*\*+\//.exec(c); - if (a) { - length += a[0].length; - c = c.slice(a[0].length); - return true; - } - return false; - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - while (cutOutBlockComments()); - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - var fragmentStart = filePath.indexOf('#'); - var fragment = ''; - if (fragmentStart!==-1) { - fragment = filePath.slice(fragmentStart); - filePath = filePath.slice(0, fragmentStart); - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; - this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - isRulesetLike: function() { - return this.rulesetLike; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - isRulesetLike: function() { - return !this.isCharset(); - }, - isCharset: function() { - return "@charset" === this.name; - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _noSpaceCombinators: { - '': true, - ' ': true, - '|': true - }, - genCSS: function (env, output) { - var spaceOrEmpty = (env.compress || this._noSpaceCombinators[this.value]) ? '' : ' '; - output.add(spaceOrEmpty + this.value + spaceOrEmpty); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = (variable !== undefined) ? variable - : (name.charAt && (name.charAt(0) === '@')); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, variable = this.variable, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - variable = false; // never treat expanded interpolation as new variable name - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline, - variable); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - charsetRuleNodes = [], - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - function isRulesetLikeNode(rule, root) { - // if it has nested rules, then it should be treated like a ruleset - if (rule.rules) - return true; - - // medias and comments do not have nested rules, but should be treated like rulesets anyway - if ( (rule instanceof tree.Media) || (root && rule instanceof tree.Comment)) - return true; - - // some directives and anonumoust nodes are ruleset like, others are not - if ((rule instanceof tree.Directive) || (rule instanceof tree.Anonymous)) { - return rule.isRulesetLike(); - } - - //anything else is assumed to be a rule - return false; - } - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (isRulesetLikeNode(rule, this.root)) { - rulesetNodes.push(rule); - } else { - //charsets should float on top of everything - if (rule.isCharset && rule.isCharset()) { - charsetRuleNodes.push(rule); - } else { - ruleNodes.push(rule); - } - } - } - ruleNodes = charsetRuleNodes.concat(ruleNodes); - - // 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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { - e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; - } - - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - if (!env.importMultiple) { - if (duplicateImport) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.rules[0]); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - if (directiveNode.rules && directiveNode.rules.rules) { - this._mergeRules(directiveNode.rules.rules); - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// -// browser.js - client-side engine -// -/*global less, window, document, XMLHttpRequest, location */ - -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol); - -less.env = less.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || - isFileProtocol ? 'development' - : 'production'); - -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - -// Load styles asynchronously (default: false) -// -// This is set to `false` by default, so that the body -// doesn't start loading before the stylesheets are parsed. -// Setting this to `true` can result in flickering. -// -less.async = less.async || false; -less.fileAsync = less.fileAsync || false; - -// Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); - -//Setup user functions -if (less.functions) { - for(var func in less.functions) { - if (less.functions.hasOwnProperty(func)) { - less.tree.functions[func] = less.functions[func]; - } - } -} - -var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); -if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; -} - -var typePattern = /^text\/(x-)?less$/; -var cache = null; -var fileCache = {}; - -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); - } -} - -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - log(content, logLevel.errors); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (!css.styleSheet) { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // For IE. - // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. - // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head - if (css.styleSheet) { - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); - } - } -} - -function postProcessCSS(styles) { - if (less.postProcessor && typeof less.postProcessor === 'function') { - styles = less.postProcessor.call(styles, styles) || styles; - } - return styles; -} - -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!less.errorReporting || less.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (less.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!less.errorReporting || less.errorReporting === "html") { - removeErrorHTML(path); - } else if (less.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof less.errorReporting === 'function') { - less.errorReporting("remove", path); - } -} - -function loadStyles(modifyVars) { - var styles = document.getElementsByTagName('style'), - style; - for (var i = 0; i < styles.length; i++) { - style = styles[i]; - if (style.type.match(typePattern)) { - var env = new less.tree.parseEnv(less), - lessText = style.innerHTML || ''; - env.filename = document.location.href.replace(/#.*$/, ''); - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - /*jshint loopfunc:true */ - // use closure to store current value of i - var callback = (function(style) { - return function (e, cssAST) { - if (e) { - return error(e, "inline"); - } - var css = cssAST.toCSS(less); - style.type = 'text/css'; - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - style.innerHTML = css; - } - }; - })(style); - new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars}); - } - } -} - -function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; -} - -function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = extractUrlParts(url), - baseUrlParts = extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; -} - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -function doXHR(url, type, callback, errback) { - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? less.fileAsync : less.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !less.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } -} - -function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) { - - if (currentFileInfo && currentFileInfo.currentDirectory && !/^([A-Za-z-]+:)?\//.test(originalHref)) { - originalHref = currentFileInfo.currentDirectory + originalHref; - } - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = extractUrlParts(originalHref, window.location.href); - var href = hrefParts.url; - var newFileInfo = { - currentDirectory: hrefParts.path, - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = hrefParts.path; - newFileInfo.rootpath = less.rootpath || hrefParts.path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - if (newFileInfo.relativeUrls) { - if (env.rootpath) { - newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path; - } else { - newFileInfo.rootpath = hrefParts.path; - } - } - - if (env.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, newFileInfo, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - doXHR(href, env.mime, function (data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - try { - callback(null, data, href, newFileInfo, { lastModified: lastModified }); - } catch (e) { - callback(e, null, href); - } - }, function (status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); -} - -function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { - - var env = new less.tree.parseEnv(less); - env.mime = sheet.type; - - if (modifyVars || less.globalVars) { - env.useFileCache = true; - } - - loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) { - - if (webInfo) { - webInfo.remaining = remaining; - - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy - createCSS(css, sheet); - webInfo.local = true; - callback(null, null, data, sheet, webInfo, path); - return; - } - } - - //TODO add tests around how this behaves when reloading - removeError(path); - - if (data) { - env.currentFileInfo = newFileInfo; - new(less.Parser)(env).parse(data, function (e, root) { - if (e) { return callback(e, null, null, sheet); } - try { - callback(e, root, data, sheet, webInfo, path); - } catch (e) { - callback(e, null, null, sheet); - } - }, {modifyVars: modifyVars, globalVars: less.globalVars}); - } else { - callback(e, null, null, sheet, webInfo, path); - } - }, env, modifyVars); -} - -function loadStyleSheets(callback, reload, modifyVars) { - for (var i = 0; i < less.sheets.length; i++) { - loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); - } -} - -function initRunningMode(){ - if (less.env === 'development') { - less.optimization = 0; - less.watchTimer = setInterval(function () { - if (less.watchMode) { - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - error(e, sheet.href); - } else if (root) { - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - }); - } - }, less.poll); - } else { - less.optimization = 3; - } -} - - - -// -// Watch mode -// -less.watch = function () { - if (!less.watchMode ){ - less.env = 'development'; - initRunningMode(); - } - this.watchMode = true; - return true; -}; - -less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; - -if (/!watch/.test(location.hash)) { - less.watch(); -} - -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - -// -// Get all tags with the 'rel' attribute set to "stylesheet/less" -// -var links = document.getElementsByTagName('link'); - -less.sheets = []; - -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} - -// -// With this function, it's possible to alter variables and re-render -// CSS without reloading less-files -// -less.modifyVars = function(record) { - less.refresh(false, record); -}; - -less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, root, _, sheet, env) { - if (e) { - return error(e, sheet.href); - } - if (env.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); - } else { - log("parsed " + sheet.href + " successfully.", logLevel.debug); - var styles = root.toCSS(less); - styles = postProcessCSS(styles); - createCSS(styles, sheet, env.lastModified); - } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); - if (env.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); -}; - -less.refreshStyles = loadStyles; - -less.Parser.fileLoader = loadFile; - -less.refresh(less.env === 'development'); - -// amd.js -// -// Define Less as an AMD module. -if (typeof define === "function" && define.amd) { - define(function () { return less; } ); -} - -})(window); \ No newline at end of file diff --git a/dist/less-1.7.5.min.js b/dist/less-1.7.5.min.js deleted file mode 100644 index 958b1212e..000000000 --- a/dist/less-1.7.5.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * Less - Leaner CSS v1.7.5 - * http://lesscss.org - * - * Copyright (c) 2009-2014, Alexis Sellier - * Licensed under the Apache v2 License. - * - */ - - /** * @license Apache v2 - */ - -!function(a,b){function c(b){return a.less[b.split("/")[1]]}function d(a,b){"undefined"!=typeof console&&w.logLevel>=b&&console.log("less: "+a)}function e(a){return a.replace(/^[a-z-]+:\/+?[^\/]+/,"").replace(/^\//,"").replace(/\.[a-zA-Z]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function f(a,c){var e="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,c,d){a.extract[c]!==b&&g.push(e.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")):a.stack&&(h+=a.stack),d(h,z.errors)}function g(a,b,c){var f=b.href||"",g="less:"+(b.title||e(f)),h=document.getElementById(g),i=!1,j=document.createElement("style");j.setAttribute("type","text/css"),b.media&&j.setAttribute("media",b.media),j.id=g,j.styleSheet||(j.appendChild(document.createTextNode(a)),i=null!==h&&h.childNodes.length>0&&j.childNodes.length>0&&h.firstChild.nodeValue===j.firstChild.nodeValue);var k=document.getElementsByTagName("head")[0];if(null===h||i===!1){var l=b&&b.nextSibling||null;l?l.parentNode.insertBefore(j,l):k.appendChild(j)}if(h&&i===!1&&h.parentNode.removeChild(h),j.styleSheet)try{j.styleSheet.cssText=a}catch(m){throw new Error("Couldn't reassign styleSheet.cssText.")}if(c&&D){d("saving "+f+" to cache.",z.info);try{D.setItem(f,a),D.setItem(f+":timestamp",c)}catch(m){d("failed to save",z.errors)}}}function h(a){return w.postProcessor&&"function"==typeof w.postProcessor&&(a=w.postProcessor.call(a,a)||a),a}function i(a,c){var d,f,h="less-error-message:"+e(c||""),i='
  • {content}
  • ',j=document.createElement("div"),k=[],l=a.filename||c,m=l.match(/([^\/]+(\?.*)?)$/)[1];j.id=h,j.className="less-error-message",f="

    "+(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+'

    in '+m+" ";var n=function(a,c,d){a.extract[c]!==b&&k.push(i.replace(/\{line\}/,(parseInt(a.line,10)||0)+(c-1)).replace(/\{class\}/,d).replace(/\{content\}/,a.extract[c]))};a.extract?(n(a,0,""),n(a,1,"line"),n(a,2,""),f+="on line "+a.line+", column "+(a.column+1)+":

      "+k.join("")+"
    "):a.stack&&(f+="
    "+a.stack.split("\n").slice(1).join("
    ")),j.innerHTML=f,g([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),j.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"==w.env&&(d=setInterval(function(){document.body&&(document.getElementById(h)?document.body.replaceChild(j,document.getElementById(h)):document.body.insertBefore(j,document.body.firstChild),clearInterval(d))},10))}function j(a,b){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?f(a,b):"function"==typeof w.errorReporting&&w.errorReporting("add",a,b):i(a,b)}function k(a){var b=document.getElementById("less-error-message:"+e(a));b&&b.parentNode.removeChild(b)}function l(){}function m(a){w.errorReporting&&"html"!==w.errorReporting?"console"===w.errorReporting?l(a):"function"==typeof w.errorReporting&&w.errorReporting("remove",a):k(a)}function n(a){for(var b,c=document.getElementsByTagName("style"),d=0;d0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=f[1]+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g}function p(a,b){var c,d,e,f,g=o(a),h=o(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var g=q(),h=y?w.fileAsync:w.async;"function"==typeof g.overrideMimeType&&g.overrideMimeType("text/css"),d("XHR: Getting '"+a+"'",z.debug),g.open("GET",a,h),g.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),g.send(null),y&&!w.fileAsync?0===g.status||g.status>=200&&g.status<300?c(g.responseText):e(g.status,a):h?g.onreadystatechange=function(){4==g.readyState&&f(g,c,e)}:f(g,c,e)}function s(b,c,d,e){c&&c.currentDirectory&&!/^([A-Za-z-]+:)?\//.test(b)&&(b=c.currentDirectory+b);var f=o(b,a.location.href),g=f.url,h={currentDirectory:f.path,filename:g};if(c?(h.entryPath=c.entryPath,h.rootpath=c.rootpath,h.rootFilename=c.rootFilename,h.relativeUrls=c.relativeUrls):(h.entryPath=f.path,h.rootpath=w.rootpath||f.path,h.rootFilename=g,h.relativeUrls=e.relativeUrls),h.relativeUrls&&(h.rootpath=e.rootpath?o(e.rootpath+p(f.path,h.entryPath)).path:f.path),e.useFileCache&&E[g])try{var i=E[g];d(null,i,g,h,{lastModified:new Date})}catch(j){d(j,null,g)}else r(g,e.mime,function(a,b){E[g]=a;try{d(null,a,g,h,{lastModified:b})}catch(c){d(c,null,g)}},function(a,b){d({type:"File",message:"'"+b+"' wasn't found ("+a+")"},null,g)})}function t(a,b,c,d,e){var f=new w.tree.parseEnv(w);f.mime=a.type,(e||w.globalVars)&&(f.useFileCache=!0),s(a.href,null,function(h,i,j,k,l){if(l){l.remaining=d;var n=D&&D.getItem(j),o=D&&D.getItem(j+":timestamp");if(!c&&o&&l.lastModified&&new Date(l.lastModified).valueOf()===new Date(o).valueOf())return g(n,a),l.local=!0,void b(null,null,i,a,l,j)}m(j),i?(f.currentFileInfo=k,new w.Parser(f).parse(i,function(c,d){if(c)return b(c,null,null,a);try{b(c,d,i,a,l,j)}catch(c){b(c,null,null,a)}},{modifyVars:e,globalVars:w.globalVars})):b(h,null,null,a,l,j)},f,e)}function u(a,b,c){for(var d=0;dD&&(C=C.slice(y-D),D=y)}function h(a,b){var c=a.charCodeAt(0|b);return 32>=c&&(32===c||10===c||9===c)}function i(a){var b,c,d=typeof a;return"string"===d?v.charAt(y)!==a?null:(l(1),a):(g(),(b=a.exec(C))?(c=b[0].length,l(c),"string"==typeof b?b:1===b.length?b[0]:b):null)}function j(a){y>D&&(C=C.slice(y-D),D=y);var b=a.exec(C);return b?(l(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null}function k(a){return v.charAt(y)!==a?null:(l(1),a)}function l(a){for(var b,c=y,d=z,e=y-D,f=y+C.length-e,g=y+=a,h=v;f>y&&(b=h.charCodeAt(y),!(b>32))&&(32===b||10===b||9===b||13===b);y++);return C=C.slice(a+y-g+e),D=y,!C.length&&z=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}function t(a,b,d){var e=d.currentFileInfo.filename;return"browser"!==w.mode&&"rhino"!==w.mode&&(e=c("path").resolve(e)),{lineNumber:s(a,b).line+1,fileName:e}}function u(a,b){var c=r(a,b),d=s(a.index,c),e=d.line,f=d.column,g=a.call&&s(a.call,c).line,h=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.currentFileInfo.filename,this.index=a.index,this.line="number"==typeof e?e+1:null,this.callLine=g+1,this.callExtract=h[g],this.stack=a.stack,this.column=f,this.extract=[h[e-1],h[e],h[e+1]]}var v,y,z,A,B,C,D,E,F,G=[],H=a&&a.filename;a instanceof x.parseEnv||(a=new x.parseEnv(a));var I=this.imports={paths:a.paths||[],queue:[],files:a.files,contents:a.contents,contentsIgnoredChars:a.contentsIgnoredChars,mime:a.mime,error:null,push:function(b,c,d,e){var f=this;this.queue.push(b);var g=function(a,c,d){f.queue.splice(f.queue.indexOf(b),1);var g=d===H;f.files[d]=c,a&&!f.error&&(f.error=a),e(a,c,g,d)};w.Parser.importer?w.Parser.importer(b,c,g,a):w.Parser.fileLoader(b,c,function(b,e,f,h){if(b)return void g(b);var i=new x.parseEnv(a);i.currentFileInfo=h,i.processImports=!1,i.contents[f]=e,(c.reference||d.reference)&&(h.reference=!0),d.inline?g(null,e,f):new w.Parser(i).parse(e,function(a,b){g(a,b,f)})},a)}},J=j;return u.prototype=new Error,u.prototype.constructor=u,this.env=a=a||{},this.optimization="optimization"in this.env?this.env.optimization:1,E={imports:I,parse:function(d,e,f){var g,h,i,j,k,l=null,m="";if(y=z=D=A=0,j=f&&f.globalVars?w.Parser.serializeVars(f.globalVars)+"\n":"",k=f&&f.modifyVars?"\n"+w.Parser.serializeVars(f.modifyVars):"",(j||f&&f.banner)&&(m=(f&&f.banner?f.banner:"")+j,E.imports.contentsIgnoredChars[a.currentFileInfo.filename]=m.length),d=d.replace(/\r\n/g,"\n"),v=d=m+d.replace(/^\uFEFF/,"")+k,E.imports.contents[a.currentFileInfo.filename]=d,B=function(b){function c(b,c){l=new u({index:c||i,type:"Parse",message:b,filename:a.currentFileInfo.filename},a)}function d(a){var c=i-s;512>c&&!a||!c||(r.push(b.slice(s,i+1)),s=i+1)}var e,f,g,h,i,j,k,m,n,o=b.length,p=0,q=0,r=[],s=0;for(i=0;o>i;i++)if(k=b.charCodeAt(i),!(k>=97&&122>=k||34>k))switch(k){case 40:q++,f=i;continue;case 41:if(--q<0)return c("missing opening `(`");continue;case 59:q||d();continue;case 123:p++,e=i;continue;case 125:if(--p<0)return c("missing opening `{`");p||q||d();continue;case 92:if(o-1>i){i++;continue}return c("unescaped `\\`");case 34:case 39:case 96:for(n=0,j=i,i+=1;o>i;i++)if(m=b.charCodeAt(i),!(m>96)){if(m==k){n=1;break}if(92==m){if(i==o-1)return c("unescaped `\\`");i++}}if(n)continue;return c("unmatched `"+String.fromCharCode(k)+"`",j);case 47:if(q||i==o-1)continue;if(m=b.charCodeAt(i+1),47==m)for(i+=2;o>i&&(m=b.charCodeAt(i),!(13>=m)||10!=m&&13!=m);i++);else if(42==m){for(g=j=i,i+=2;o-1>i&&(m=b.charCodeAt(i),125==m&&(h=i),42!=m||47!=b.charCodeAt(i+1));i++);if(i==o-1)return c("missing closing `*/`",j);i++}continue;case 42:if(o-1>i&&47==b.charCodeAt(i+1))return c("unmatched `/*`");continue}return 0!==p?g>e&&h>g?c("missing closing `}` or `*/`",e):c("missing closing `}`",e):0!==q?c("missing closing `)`",f):(d(!0),r)}(d),l)return e(new u(l,a));C=B[0];try{g=new x.Ruleset(null,this.parsers.primary()),g.root=!0,g.firstRoot=!0}catch(n){return e(new u(n,a))}if(g.toCSS=function(d){return function(e,f){e=e||{};var g,h,i=new x.evalEnv(e);"object"!=typeof f||Array.isArray(f)||(f=Object.keys(f).map(function(a){var b=f[a];return b instanceof x.Value||(b instanceof x.Expression||(b=new x.Expression([b])),b=new x.Value([b])),new x.Rule("@"+a,b,!1,null,0)}),i.frames=[new x.Ruleset(null,f)]);try{var j,k=[],l=[new x.joinSelectorVisitor,new x.processExtendsVisitor,new x.toCSSVisitor({compress:Boolean(e.compress)})],m=this;if(e.plugins)for(j=0;j57||43>b||47===b||44==b))return a=j(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/),a?new x.Dimension(a[1],a[2]):void 0},unicodeDescriptor:function(){var a;return a=j(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new x.UnicodeDescriptor(a[0]):void 0},javascript:function(){var c,d,e=y;return"~"===v.charAt(e)&&(e++,d=!0),"`"===v.charAt(e)?(a.javascriptEnabled===b||a.javascriptEnabled||o("You are using JavaScript, which has been disabled."),d&&k("~"),c=j(/^`([^`]*)`/),c?new x.JavaScript(c[1],y,d):void 0):void 0}},variable:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===v.charAt(y)&&(a=j(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new x.RulesetCall(a[1]):void 0},extend:function(a){var b,c,d,e,f,g=y;if(j(a?/^&:extend\(/:/^:extend\(/)){do{for(d=null,b=null;!(d=j(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];d=d&&d[1],b||o("Missing target selector for :extend()."),f=new x.Extend(new x.Selector(b),d,g),e?e.push(f):e=[f]}while(k(","));return m(/^\)/),a&&m(/^;/),e}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var b,c,g,h,i,l,m=v.charAt(y),o=!1,p=y;if("."===m||"#"===m){for(d();;){if(b=y,h=j(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!h)break;g=new x.Element(i,h,b,a.currentFileInfo),c?c.push(g):c=[g],i=k(">")}return c&&(k("(")&&(l=this.args(!0).args,n(")")),F.important()&&(o=!0),F.end())?(f(),new x.mixin.Call(c,l,p,a.currentFileInfo,o)):void e()}},args:function(a){var b,c,g,h,i,l,m=E.parsers,n=m.entities,p={args:null,variadic:!1},q=[],r=[],s=[];for(d();;){if(a)l=m.detachedRuleset()||m.expression();else{if(m.comments(),"."===v.charAt(y)&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({variadic:!0});break}l=n.variable()||n.literal()||n.keyword()}if(!l)break;h=null,l.throwAwayComments&&l.throwAwayComments(),i=l;var t=null;if(a?l.value&&1==l.value.length&&(t=l.value[0]):t=l,t&&t instanceof x.Variable)if(k(":")){if(q.length>0&&(b&&o("Cannot mix ; and , as delimiter types"),c=!0),i=a&&m.detachedRuleset()||m.expression(),!i){if(!a)return e(),p.args=[],p;o("could not understand value for named argument")}h=g=t.name}else{if(!a&&j(/^\.{3}/)){p.variadic=!0,k(";")&&!b&&(b=!0),(b?r:s).push({name:l.name,variadic:!0});break}a||(g=h=t.name,i=null)}i&&q.push(i),s.push({name:h,value:i}),k(",")||(k(";")||b)&&(c&&o("Cannot mix ; and , as delimiter types"),b=!0,q.length>1&&(i=new x.Value(q)),r.push({name:g,value:i}),g=null,q=[],c=!1)}return f(),p.args=b?r:s,p},definition:function(){var a,b,c,g,h=[],i=!1;if(!("."!==v.charAt(y)&&"#"!==v.charAt(y)||p(/^[^{]*\}/)))if(d(),b=j(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var l=this.args(!1);if(h=l.args,i=l.variadic,!k(")"))return A=y,void e();if(F.comments(),j(/^when/)&&(g=m(F.conditions,"expected condition")),c=F.block())return f(),new x.mixin.Definition(a,h,c,g,i);e()}else f()}},entity:function(){var a=this.entities;return a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()||this.comment()},end:function(){return k(";")||q("}")},alpha:function(){var a;if(j(/^\(opacity=/i))return a=j(/^\d+/)||this.entities.variable(),a?(n(")"),new x.Alpha(a)):void 0},element:function(){var b,c,g,h=y;return c=this.combinator(),b=j(/^(?:\d+\.\d+|\d+)%/)||j(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||k("*")||k("&")||this.attribute()||j(/^\([^()@]+\)/)||j(/^[\.#](?=@)/)||this.entities.variableCurly(),b||(d(),k("(")?(g=this.selector())&&k(")")?(b=new x.Paren(g),f()):e():f()),b?new x.Element(c,b,h,a.currentFileInfo):void 0},combinator:function(){var a=v.charAt(y);if("/"===a){d();var b=j(/^\/[a-z]+\//i);if(b)return f(),new x.Combinator(b);e()}if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(y++,"^"===a&&"^"===v.charAt(y)&&(a="^^",y++);h(v,y);)y++;return new x.Combinator(a)}return new x.Combinator(h(v,y-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(b){for(var c,d,e,f,g,h,i,j=y,k=J;(b&&(g=this.extend())||b&&(h=k(/^when/))||(f=this.element()))&&(h?i=m(this.conditions,"expected condition"):i?o("CSS guard can only be used at the end of selector"):g?d?d.push(g):d=[g]:(d&&o("Extend can only be used at the end of selector"),e=v.charAt(y),c?c.push(f):c=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return c?new x.Selector(c,d,i,j,a.currentFileInfo):void(d&&o("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(k("[")){var a,b,c,d=this.entities;return(a=d.variableCurly())||(a=m(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=j(/^[|~*$^]?=/),c&&(b=d.quoted()||j(/^[0-9]+%/)||j(/^[\w-]+/)||d.variableCurly()),n("]"),new x.Attribute(a,c,b)}},block:function(){var a;return k("{")&&(a=this.primary())&&k("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new x.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new x.DetachedRuleset(a):void 0},ruleset:function(){var b,c,g,h;for(d(),a.dumpLineNumbers&&(h=t(y,v,a));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],this.comments(),c.condition&&b.length>1&&o("Guards are only currently allowed on a single selector."),!k(","))break;c.condition&&o("Guards are only currently allowed on a single selector."),this.comments()}if(b&&(g=this.block())){f();var i=new x.Ruleset(b,g,a.strictImports);return a.dumpLineNumbers&&(i.debugInfo=h),i}A=y,e()},rule:function(b){var c,g,h,i,j,k=y,l=v.charAt(k);if("."!==l&&"#"!==l&&"&"!==l)if(d(),c=this.variable()||this.ruleProperty()){if(j="string"==typeof c,j&&(g=this.detachedRuleset()),this.comments(),g||(g=b||!a.compress&&!j?this.anonymousValue()||this.value():this.value()||this.anonymousValue(),h=this.important(),i=!j&&c.pop().value),g&&this.end())return f(),new x.Rule(c,g,h,i,k,a.currentFileInfo);if(A=y,e(),g&&!b)return this.rule(!0)}else f()},anonymousValue:function(){var a;return a=/^([^@+\/'"*`(;{}-]*);/.exec(C),a?(y+=a[0].length-1,new x.Anonymous(a[1])):void 0},"import":function(){var b,c,d=y,e=j(/^@import?\s+/);if(e){var f=(e?this.importOptions():null)||{};if(b=this.entities.quoted()||this.entities.url())return c=this.mediaFeatures(),i(";")||(y=d,o("missing semi-colon or unrecognised media features on import")),c=c&&new x.Value(c),new x.Import(b,c,f,d,a.currentFileInfo);y=d,o("malformed import statement")}},importOptions:function(){var a,b,c,d={};if(!k("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!k(","))break}while(a);return n(")"),d},importOption:function(){var a=j(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var b,c,d=this.entities,e=[];do if(b=d.keyword()||d.variable())e.push(b);else if(k("(")){if(c=this.property(),b=this.value(),!k(")"))return null;if(c&&b)e.push(new x.Paren(new x.Rule(c,b,null,null,y,a.currentFileInfo,!0)));else{if(!b)return null;e.push(new x.Paren(b))}}while(b);return e.length>0?new x.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!k(","))break}else if(a=b.variable(),a&&(c.push(a),!k(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,d,e;return a.dumpLineNumbers&&(e=t(y,v,a)),j(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(d=new x.Media(c,b,y,a.currentFileInfo),a.dumpLineNumbers&&(d.debugInfo=e),d):void 0},directive:function(){var b,c,g,h,i,l,m,n=y,p=!0;if("@"===v.charAt(y)){if(c=this["import"]()||this.media())return c;if(d(),b=j(/^@[a-z-]+/)){switch(h=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(h="@"+b.slice(b.indexOf("-",2)+1)),h){case"@charset":i=!0,p=!1;break;case"@namespace":l=!0,p=!1;break;case"@keyframes":i=!0;break;case"@host":case"@page":case"@document":case"@supports":m=!0}return this.comments(),i?(c=this.entity(),c||o("expected "+b+" identifier")):l?(c=this.expression(),c||o("expected "+b+" expression")):m&&(c=(j(/^[^{;]+/)||"").trim(),c&&(c=new x.Anonymous(c))),this.comments(),p&&(g=this.blockRuleset()),g||!p&&c&&k(";")?(f(),new x.Directive(b,c,g,n,a.currentFileInfo,a.dumpLineNumbers?t(n,v,a):null)):void e()}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!k(",")))break;while(a);return b.length>0?new x.Value(b):void 0},important:function(){return"!"===v.charAt(y)?j(/^! *important/):void 0},sub:function(){var a,b;return k("(")&&(a=this.addition())?(b=new x.Expression([a]),n(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,g,i;if(a=this.operand()){for(i=h(v,y-1);;){if(p(/^\/[*\/]/))break;if(d(),c=k("/")||k("*"),!c){f();break}if(b=this.operand(),!b){e();break}f(),a.parensInOp=!0,b.parensInOp=!0,g=new x.Operation(c,[g||a,b],i),i=h(v,y-1)}return g||a}},addition:function(){var a,b,c,d,e;if(a=this.multiplication()){for(e=h(v,y-1);;){if(c=j(/^[-+]\s+/)||!e&&(k("+")||k("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,d=new x.Operation(c,[d||a,b],e),e=h(v,y-1)}return d||a}},conditions:function(){var a,b,c,d=y;if(a=this.condition()){for(;;){if(!p(/^,\s*(not\s*)?\(/)||!k(","))break;if(b=this.condition(),!b)break;c=new x.Condition("or",c||a,b,d)}return c||a}},condition:function(){var a,b,c,d,e=this.entities,f=y,g=!1;return j(/^not/)&&(g=!0),n("("),a=this.addition()||e.keyword()||e.quoted(),a?(d=j(/^(?:>=|<=|=<|[<=>])/),d?(b=this.addition()||e.keyword()||e.quoted(),b?c=new x.Condition(d,a,b,f,g):o("expected expression")):c=new x.Condition("=",a,new x.Keyword("true"),f,g),n(")"),j(/^and/)?new x.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities,c=v.charAt(y+1);"-"!==v.charAt(y)||"@"!==c&&"("!==c||(a=k("-"));var d=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(d.parensInOp=!0,d=new x.Negative(d)),d},expression:function(){var a,b,c=[];do a=this.addition()||this.entity(),a&&(c.push(a),p(/^\/[\/*]/)||(b=k("/"),b&&c.push(new x.Anonymous(b))));while(a);return c.length>0?new x.Expression(c):void 0},property:function(){var a=j(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function b(a){var b=a.exec(f);return b?(h.push(y+i),i+=b[0].length,f=f.slice(b[1].length),g.push(b[1])):void 0}function c(){var a=/^\s*\/\*(?:[^*]|\*+[^\/*])*\*+\//.exec(f);return a?(i+=a[0].length,f=f.slice(a[0].length),!0):!1}var d,e,f=C,g=[],h=[],i=0;for(b(/^(\*?)/);b(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/););for(;c(););if(g.length>1&&b(/^\s*((?:\+_|\+)?)\s*:/)){for(l(i),""===g[0]&&(g.shift(),h.shift()),e=0;el;l++)e=b.rgb[l]/255,f=c.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new d.Color(k,g)}function g(){var a,b=d.functions;for(a in l)l.hasOwnProperty(a)&&(b[a]=e.bind(null,Math[a],l[a]));for(a in m)m.hasOwnProperty(a)&&(b[a]=f.bind(null,m[a]));a=d.defaultFunc,b["default"]=a.eval.bind(a)}function h(a){return d.functions.hsla(a.h,a.s,a.l,a.a)}function i(a,b){return a instanceof d.Dimension&&a.unit.is("%")?parseFloat(a.value*b/100):j(a)}function j(a){if(a instanceof d.Dimension)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function k(a){return Math.min(1,Math.max(0,a))}d.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(a,b,c,e){var f=[a,b,c].map(function(a){return i(a,255)});return e=j(e),new d.Color(f,e)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,c,d){function e(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?g+(f-g)*a*6:1>2*a?f:2>3*a?g+(f-g)*(2/3-a)*6:g}a=j(a)%360/360,b=k(j(b)),c=k(j(c)),d=k(j(d));var f=.5>=c?c*(b+1):c+b-c*b,g=2*c-f;return this.rgba(255*e(a+1/3),255*e(a),255*e(a-1/3),d)},hsv:function(a,b,c){return this.hsva(a,b,c,1)},hsva:function(a,b,c,d){a=j(a)%360/360*360,b=j(b),c=j(c),d=j(d);var e,f;e=Math.floor(a/60%6),f=a/60-e;var g=[c,c*(1-b),c*(1-f*b),c*(1-(1-f)*b)],h=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return this.rgba(255*g[h[e][0]],255*g[h[e][1]],255*g[h[e][2]],d)},hue:function(a){return new d.Dimension(a.toHSL().h)},saturation:function(a){return new d.Dimension(100*a.toHSL().s,"%")},lightness:function(a){return new d.Dimension(100*a.toHSL().l,"%")},hsvhue:function(a){return new d.Dimension(a.toHSV().h)},hsvsaturation:function(a){return new d.Dimension(100*a.toHSV().s,"%")},hsvvalue:function(a){return new d.Dimension(100*a.toHSV().v,"%")},red:function(a){return new d.Dimension(a.rgb[0])},green:function(a){return new d.Dimension(a.rgb[1])},blue:function(a){return new d.Dimension(a.rgb[2])},alpha:function(a){return new d.Dimension(a.toHSL().a)},luma:function(a){return new d.Dimension(a.luma()*a.alpha*100,"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new d.Dimension(b*a.alpha*100,"%")},saturate:function(a,b){if(!a.rgb)return null;var c=a.toHSL();return c.s+=b.value/100,c.s=k(c.s),h(c)},desaturate:function(a,b){var c=a.toHSL();return c.s-=b.value/100,c.s=k(c.s),h(c)},lighten:function(a,b){var c=a.toHSL();return c.l+=b.value/100,c.l=k(c.l),h(c)},darken:function(a,b){var c=a.toHSL();return c.l-=b.value/100,c.l=k(c.l),h(c)},fadein:function(a,b){var c=a.toHSL();return c.a+=b.value/100,c.a=k(c.a),h(c)},fadeout:function(a,b){var c=a.toHSL();return c.a-=b.value/100,c.a=k(c.a),h(c)},fade:function(a,b){var c=a.toHSL();return c.a=b.value/100,c.a=k(c.a),h(c)},spin:function(a,b){var c=a.toHSL(),d=(c.h+b.value)%360;return c.h=0>d?360+d:d,h(c)},mix:function(a,b,c){c||(c=new d.Dimension(50));var e=c.value/100,f=2*e-1,g=a.toHSL().a-b.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[a.rgb[0]*h+b.rgb[0]*i,a.rgb[1]*h+b.rgb[1]*i,a.rgb[2]*h+b.rgb[2]*i],k=a.alpha*e+b.alpha*(1-e);return new d.Color(j,k)},greyscale:function(a){return this.desaturate(a,new d.Dimension(100))},contrast:function(a,b,c,d){if(!a.rgb)return null;if("undefined"==typeof c&&(c=this.rgba(255,255,255,1)),"undefined"==typeof b&&(b=this.rgba(0,0,0,1)),b.luma()>c.luma()){var e=c;c=b,b=e}return d="undefined"==typeof d?.43:j(d),a.luma()i.value)&&(m[f]=g);else{if(k!==b&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(c[e].value)&&Array.prototype.push.apply(c,Array.prototype.slice.call(c[e].value));return 1==m.length?m[0]:(c=m.map(function(a){return a.toCSS(this.env)}).join(this.env.compress?",":", "),new d.Anonymous((a?"min":"max")+"("+c+")"))},min:function(){return this._minmax(!0,arguments)},max:function(){return this._minmax(!1,arguments)},"get-unit":function(a){return new d.Anonymous(a.unit)},argb:function(a){return new d.Anonymous(a.toARGB())},percentage:function(a){return new d.Dimension(100*a.value,"%")},color:function(a){if(a instanceof d.Quoted){var b,c=a.value;if(b=d.Color.fromKeyword(c))return b;if(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(c))return new d.Color(c.slice(1));throw{type:"Argument",message:"argument must be a color keyword or 3/6 digit hex e.g. #FFF"}}throw{type:"Argument",message:"argument must be a string"}},iscolor:function(a){return this._isa(a,d.Color)},isnumber:function(a){return this._isa(a,d.Dimension)},isstring:function(a){return this._isa(a,d.Quoted)},iskeyword:function(a){return this._isa(a,d.Keyword)},isurl:function(a){return this._isa(a,d.URL)},ispixel:function(a){return this.isunit(a,"px")},ispercentage:function(a){return this.isunit(a,"%")},isem:function(a){return this.isunit(a,"em")},isunit:function(a,b){return a instanceof d.Dimension&&a.unit.is(b.value||b)?d.True:d.False},_isa:function(a,b){return a instanceof b?d.True:d.False},tint:function(a,b){return this.mix(this.rgb(255,255,255),a,b)},shade:function(a,b){return this.mix(this.rgb(0,0,0),a,b)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new d.Dimension(b)},"data-uri":function(b,e){if("undefined"!=typeof a)return new d.URL(e||b,this.currentFileInfo).eval(this.env);var f=b.value,g=e&&e.value,h=c("./fs"),i=c("path"),j=!1;arguments.length<2&&(g=f);var k=g.indexOf("#"),l="";if(-1!==k&&(l=g.slice(k),g=g.slice(0,k)),this.env.isPathRelative(g)&&(g=this.currentFileInfo.relativeUrls?i.join(this.currentFileInfo.currentDirectory,g):i.join(this.currentFileInfo.entryPath,g)),arguments.length<2){var m;try{m=c("mime")}catch(n){m=d._mime}f=m.lookup(g);var o=m.charsets.lookup(f);j=["US-ASCII","UTF-8"].indexOf(o)<0,j&&(f+=";base64")}else j=/;base64$/.test(f);var p=h.readFileSync(g),q=32,r=parseInt(p.length/1024,10);if(r>=q&&this.env.ieCompat!==!1)return this.env.silent||console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",g,r,q),new d.URL(e||b,this.currentFileInfo).eval(this.env);p=j?p.toString("base64"):encodeURIComponent(p);var s='"data:'+f+","+p+l+'"';return new d.URL(new d.Anonymous(s))},"svg-gradient":function(a){function e(){throw{type:"Argument",message:"svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]"}}arguments.length<3&&e();var f,g,h,i,j,k,l,m=Array.prototype.slice.call(arguments,1),n="linear",o='x="0" y="0" width="1" height="1"',p=!0,q={compress:!1},r=a.toCSS(q);switch(r){case"to bottom":f='x1="0%" y1="0%" x2="0%" y2="100%"';break;case"to right":f='x1="0%" y1="0%" x2="100%" y2="0%"';break;case"to bottom right":f='x1="0%" y1="0%" x2="100%" y2="100%"';break;case"to top right":f='x1="0%" y1="100%" x2="100%" y2="0%"';break;case"ellipse":case"ellipse at center":n="radial",f='cx="50%" cy="50%" r="75%"',o='x="-50" y="-50" width="101" height="101"';break;default:throw{type:"Argument",message:"svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'"}}for(g='<'+n+'Gradient id="gradient" gradientUnits="userSpaceOnUse" '+f+">",h=0;hl?' stop-opacity="'+l+'"':"")+"/>";if(g+="',p)try{g=c("./encoder").encodeBase64(g)}catch(s){p=!1}return g="'data:image/svg+xml"+(p?";base64":"")+","+g+"'",new d.URL(new d.Anonymous(g))}},d._mime={_types:{".htm":"text/html",".html":"text/html",".gif":"image/gif",".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png"},lookup:function(a){var e=c("path").extname(a),f=d._mime._types[e];if(f===b)throw new Error('Optional dependency "mime" is required for '+e);return f},charsets:{lookup:function(a){return a&&/^text\//.test(a)?"UTF-8":""}}};var l={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"},m={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?m.multiply(a,b):m.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return m.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};d.defaultFunc={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?d.True:d.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}},g(),d.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},d.functionCall=function(a,b){this.env=a,this.currentFileInfo=b},d.functionCall.prototype=d.functions}(c("./tree")),function(a){a.colors={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"}}(c("./tree")),function(a){a.debugInfo=function(b,c,d){var e="";if(b.dumpLineNumbers&&!b.compress)switch(b.dumpLineNumbers){case"comments":e=a.debugInfo.asComment(c);break;case"mediaquery":e=a.debugInfo.asMediaQuery(c);break;case"all":e=a.debugInfo.asComment(c)+(d||"")+a.debugInfo.asMediaQuery(c)}return e},a.debugInfo.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},a.debugInfo.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},a.find=function(a,b){for(var c,d=0;d1?"["+a.value.map(function(a){return a.toCSS()}).join(", ")+"]":a.toCSS()},a.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},a.outputRuleset=function(a,b,c){var d,e=c.length;if(a.tabLevel=(0|a.tabLevel)+1,a.compress){for(b.add("{"),d=0;e>d;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={type:"Alpha",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Alpha(this.value.eval(b)):this},genCSS:function(a,b){b.add("alpha(opacity="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value),b.add(")")},toCSS:a.toCSS}}(c("../tree")),function(a){a.Anonymous=function(a,b,c,d,e){this.value=a,this.index=b,this.mapLines=d,this.currentFileInfo=c,this.rulesetLike="undefined"==typeof e?!1:e},a.Anonymous.prototype={type:"Anonymous",eval:function(){return new a.Anonymous(this.value,this.index,this.currentFileInfo,this.mapLines,this.rulesetLike)},compare:function(a){if(!a.toCSS)return-1;var b=this.toCSS(),c=a.toCSS();return b===c?0:c>b?-1:1},isRulesetLike:function(){return this.rulesetLike},genCSS:function(a,b){b.add(this.value,this.currentFileInfo,this.index,this.mapLines)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={type:"Assignment",accept:function(a){this.value=a.visit(this.value)},eval:function(b){return this.value.eval?new a.Assignment(this.key,this.value.eval(b)):this},genCSS:function(a,b){b.add(this.key+"="),this.value.genCSS?this.value.genCSS(a,b):b.add(this.value)},toCSS:a.toCSS}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.currentFileInfo=d},a.Call.prototype={type:"Call",accept:function(a){this.args&&(this.args=a.visitArray(this.args))},eval:function(b){var c,d,e=this.args.map(function(a){return a.eval(b)}),f=this.name.toLowerCase();if(f in a.functions)try{if(d=new a.functionCall(b,this.currentFileInfo),c=d[f].apply(d,e),null!=c)return c}catch(g){throw{type:g.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(g.message?": "+g.message:""),index:this.index,filename:this.currentFileInfo.filename}}return new a.Call(this.name,e,this.index,this.currentFileInfo)},genCSS:function(a,b){b.add(this.name+"(",this.currentFileInfo,this.index);for(var c=0;ca?"0":"")+a.toString(16)}).join("")}function c(a,b){return Math.min(Math.max(a,0),b)}a.Color=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};var d="transparent";a.Color.prototype={type:"Color",eval:function(){return this},luma:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255;return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},genCSS:function(a,b){b.add(this.toCSS(a))},toCSS:function(b,e){var f=b&&b.compress&&!e,g=a.fround(b,this.alpha);if(1>g)return 0===g&&this.isTransparentKeyword?d:"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(g,1)).join(","+(f?"":" "))+")";var h=this.toRGB();if(f){var i=h.split("");i[1]===i[2]&&i[3]===i[4]&&i[5]===i[6]&&(h="#"+i[1]+i[3]+i[5])}return h},operate:function(b,c,d){for(var e=[],f=this.alpha*(1-d.alpha)+d.alpha,g=0;3>g;g++)e[g]=a.operate(b,c,this.rgb[g],d.rgb[g]);return new a.Color(e,f)},toRGB:function(){return b(this.rgb)},toHSL:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},toHSV:function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},toARGB:function(){return b([255*this.alpha].concat(this.rgb))},compare:function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:-1}},a.Color.fromKeyword=function(b){if(b=b.toLowerCase(),a.colors.hasOwnProperty(b))return new a.Color(a.colors[b].slice(1));if(b===d){var c=new a.Color([0,0,0],0);return c.isTransparentKeyword=!0,c}}}(c("../tree")),function(a){a.Comment=function(a,b,c,d){this.value=a,this.silent=!!b,this.currentFileInfo=d},a.Comment.prototype={type:"Comment",genCSS:function(b,c){this.debugInfo&&c.add(a.debugInfo(b,this),this.currentFileInfo,this.index),c.add(this.value.trim())},toCSS:a.toCSS,isSilent:function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&!this.value.match(/^\/\*!/);return this.silent||b||c},eval:function(){return this},markReferenced:function(){this.isReferenced=!0}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype={type:"Condition",accept:function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},eval:function(a){var b,c=this.lvalue.eval(a),d=this.rvalue.eval(a),e=this.index;return b=function(a){switch(a){case"and":return c&&d;case"or":return c||d;default:if(c.compare)b=c.compare(d);else{if(!d.compare)throw{type:"Type",message:"Unable to perform comparison",index:e};b=d.compare(c)}switch(b){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a}}}(this.op),this.negate?!b:b}}}(c("../tree")),function(a){a.DetachedRuleset=function(a,b){this.ruleset=a,this.frames=b},a.DetachedRuleset.prototype={type:"DetachedRuleset",accept:function(a){this.ruleset=a.visit(this.ruleset)},eval:function(b){var c=this.frames||b.frames.slice(0);return new a.DetachedRuleset(this.ruleset,c)},callEval:function(b){return this.ruleset.eval(this.frames?new a.evalEnv(b,this.frames.concat(b.frames)):b)}}}(c("../tree")),function(a){a.Dimension=function(c,d){this.value=parseFloat(c),this.unit=d&&d instanceof a.Unit?d:new a.Unit(d?[d]:b)},a.Dimension.prototype={type:"Dimension",accept:function(a){this.unit=a.visit(this.unit)},eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},genCSS:function(b,c){if(b&&b.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var d=a.fround(b,this.value),e=String(d);if(0!==d&&1e-6>d&&d>-1e-6&&(e=d.toFixed(20).replace(/0+$/,"")),b&&b.compress){if(0===d&&this.unit.isLength())return void c.add(e);d>0&&1>d&&(e=e.substr(1))}c.add(e),this.unit.genCSS(b,c)},toCSS:a.toCSS,operate:function(b,c,d){var e=a.operate(b,c,this.value,d.value),f=this.unit.clone();if("+"===c||"-"===c)if(0===f.numerator.length&&0===f.denominator.length)f.numerator=d.unit.numerator.slice(0),f.denominator=d.unit.denominator.slice(0);else if(0===d.unit.numerator.length&&0===f.denominator.length);else{if(d=d.convertTo(this.unit.usedUnits()),b.strictUnits&&d.unit.toString()!==f.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+f.toString()+"' and '"+d.unit.toString()+"'.");e=a.operate(b,c,this.value,d.value)}else"*"===c?(f.numerator=f.numerator.concat(d.unit.numerator).sort(),f.denominator=f.denominator.concat(d.unit.denominator).sort(),f.cancel()):"/"===c&&(f.numerator=f.numerator.concat(d.unit.denominator).sort(),f.denominator=f.denominator.concat(d.unit.numerator).sort(),f.cancel());return new a.Dimension(e,f)},compare:function(b){if(b instanceof a.Dimension){var c,d,e,f;if(this.unit.isEmpty()||b.unit.isEmpty())c=this,d=b;else if(c=this.unify(),d=b.unify(),0!==c.unit.compare(d.unit))return-1;return e=c.value,f=d.value,f>e?-1:e>f?1:0}return-1},unify:function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},convertTo:function(b){var c,d,e,f,g,h=this.value,i=this.unit.clone(),j={};if("string"==typeof b){for(c in a.UnitConversions)a.UnitConversions[c].hasOwnProperty(b)&&(j={},j[c]=b);b=j}g=function(a,b){return e.hasOwnProperty(a)?(b?h/=e[a]/e[f]:h*=e[a]/e[f],f):a};for(d in b)b.hasOwnProperty(d)&&(f=b[d],e=a.UnitConversions[d],i.map(g));return i.cancel(),new a.Dimension(h,i)}},a.UnitConversions={length:{m:1,cm:.01,mm:.001,"in":.0254,px:.0254/96,pt:.0254/72,pc:.0254/72*12},duration:{s:1,ms:.001},angle:{rad:1/(2*Math.PI),deg:1/360,grad:.0025,turn:1}},a.Unit=function(a,b,c){this.numerator=a?a.slice(0).sort():[],this.denominator=b?b.slice(0).sort():[],this.backupUnit=c},a.Unit.prototype={type:"Unit",clone:function(){return new a.Unit(this.numerator.slice(0),this.denominator.slice(0),this.backupUnit)},genCSS:function(a,b){this.numerator.length>=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},toCSS:a.toCSS,toString:function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()}}}(c("../tree")),function(a){a.Directive=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f},a.Directive.prototype={type:"Directive",accept:function(a){var b=this.value,c=this.rules;c&&(c=a.visit(c)),b&&(b=a.visit(b))},isRulesetLike:function(){return!this.isCharset()},isCharset:function(){return"@charset"===this.name},genCSS:function(b,c){var d=this.value,e=this.rules;c.add(this.name,this.currentFileInfo,this.index),d&&(c.add(" "),d.genCSS(b,c)),e?a.outputRuleset(b,c,[e]):c.add(";")},toCSS:a.toCSS,eval:function(b){var c=this.value,d=this.rules;return c&&(c=c.eval(b)),d&&(d=d.eval(b),d.root=!0),new a.Directive(this.name,c,d,this.index,this.currentFileInfo,this.debugInfo)},variable:function(b){return this.rules?a.Ruleset.prototype.variable.call(this.rules,b):void 0},find:function(){return this.rules?a.Ruleset.prototype.find.apply(this.rules,arguments):void 0},rulesets:function(){return this.rules?a.Ruleset.prototype.rulesets.apply(this.rules):void 0},markReferenced:function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;a1?c=new a.Expression(this.value.map(function(a){return a.eval(b)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),c=this.value[0].eval(b)):c=this,d&&b.outOfParenthesis(),this.parens&&this.parensInOp&&!b.isMathOn()&&!e&&(c=new a.Paren(c)),c},genCSS:function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]}}}(c("../tree")),function(a){a.Import=function(a,c,d,e,f){if(this.options=d,this.index=e,this.path=a,this.features=c,this.currentFileInfo=f,this.options.less!==b||this.options.inline)this.css=!this.options.less||this.options.inline;else{var g=this.getPath();g&&/css([\?;].*)?$/.test(g)&&(this.css=!0)}},a.Import.prototype={type:"Import",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},genCSS:function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},toCSS:a.toCSS,getPath:function(){if(this.path instanceof a.Quoted){var c=this.path.value;return this.css!==b||/(\.[a-z]*$)|([\?;].*)$/.test(c)?c:c+".less"}return this.path instanceof a.URL?this.path.value.value:null},evalForImport:function(b){return new a.Import(this.path.eval(b),this.features,this.options,this.index,this.currentFileInfo)},evalPath:function(b){var c=this.path.eval(b),d=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(c instanceof a.URL)){if(d){var e=c.value;e&&b.isPathRelative(e)&&(c.value=d+e)}c.value=b.normalizePath(c.value)}return c},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new a.Anonymous(this.root,0,{filename:this.importedFilename},!0,!0);return this.features?new a.Media([e],this.features.value):[e]}if(this.css){var f=new a.Import(this.evalPath(b),d,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return c=new a.Ruleset(null,this.root.rules.slice(0)),c.evalImports(b),this.features?new a.Media(c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={type:"JavaScript",eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify(new a.Variable("@"+e,d.index).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+f+"`",index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(e[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",index:this.index}}return"number"==typeof c?new a.Dimension(c):"string"==typeof c?new a.Quoted('"'+c+'"',c,this.escaped,this.index):new a.Anonymous(Array.isArray(c)?c.join(", "):c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={type:"Keyword",eval:function(){return this},genCSS:function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},toCSS:a.toCSS,compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.Media=function(b,c,d,e){this.index=d,this.currentFileInfo=e;var f=this.emptySelectors();this.features=new a.Value(c),this.rules=[new a.Ruleset(f,b)],this.rules[0].allowImports=!0},a.Media.prototype={type:"Media",accept:function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},genCSS:function(b,c){c.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(b,c),a.outputRuleset(b,c,this.rules)},toCSS:a.toCSS,eval:function(b){b.mediaBlocks||(b.mediaBlocks=[],b.mediaPath=[]);var c=new a.Media(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,c.debugInfo=this.debugInfo);var d=!1;b.strictMath||(d=!0,b.strictMath=!0);try{c.features=this.features.eval(b)}finally{d&&(b.strictMath=!1)}return b.mediaPath.push(c),b.mediaBlocks.push(c),b.frames.unshift(this.rules[0]),c.rules=[this.rules[0].eval(b)],b.frames.shift(),b.mediaPath.pop(),0===b.mediaPath.length?c.evalTop(b):c.evalNested(b)},variable:function(b){return a.Ruleset.prototype.variable.call(this.rules[0],b)},find:function(){return a.Ruleset.prototype.find.apply(this.rules[0],arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.rules[0])},emptySelectors:function(){var b=new a.Element("","&",this.index,this.currentFileInfo),c=[new a.Selector([b],null,null,this.index,this.currentFileInfo)];return c[0].mediaEmpty=!0,c},markReferenced:function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();c=new a.Ruleset(d,b.mediaBlocks),c.multiMedia=!0}return delete b.mediaBlocks,delete b.mediaPath,c},evalNested:function(b){var c,d,e=b.mediaPath.concat([this]);for(c=0;c0;c--)b.splice(c,0,new a.Anonymous("and"));return new a.Expression(b)})),new a.Ruleset([],[])},permute:function(a){if(0===a.length)return[]; -if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;d0){for(j=!0,g=0;gh;h++)t.value(h),s[h]=d.matchCondition(e,b);(s[0]||s[1])&&(s[0]!=s[1]&&(l.group=s[1]?v:w),r.push(l))}else r.push(l);q=!0}}for(t.reset(),n=[0,0,0],g=0;g0)m=w;else if(m=v,n[v]+n[w]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(e)+"`",index:this.index,filename:this.currentFileInfo.filename};for(g=0;gh;h++)if(g=d[h],k=g&&g.name){for(l=!1,i=0;ii;i++)f.push(d[i].value.eval(b));n.prependRule(new a.Rule(k,new a.Expression(f).eval(b)))}else{if(j=g&&g.value)j=j.eval(b);else{if(!o[h].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+p+" for "+this.arity+")"};j=o[h].value.eval(c),n.resetCache()}n.prependRule(new a.Rule(k,j)),e[h]=j}if(o[h].variadic&&d)for(i=m;p>i;i++)e[i]=d[i].value.eval(b);m++}return n},eval:function(b){return new a.mixin.Definition(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||b.frames.slice(0))},evalCall:function(b,c,d){var e,f,g=[],h=this.frames?this.frames.concat(b.frames):b.frames,i=this.evalParams(b,new a.evalEnv(b,h),c,g);return i.prependRule(new a.Rule("@arguments",new a.Expression(g).eval(b))),e=this.rules.slice(0),f=new a.Ruleset(null,e),f.originalRuleset=this,f=f.eval(new a.evalEnv(b,[this,i].concat(h))),d&&(f=this.parent.makeImportant.apply(f)),f},matchCondition:function(b,c){return this.condition&&!this.condition.eval(new a.evalEnv(c,[this.evalParams(c,new a.evalEnv(c,this.frames?this.frames.concat(c.frames):c.frames),b,[])].concat(this.frames).concat(c.frames)))?!1:!0},matchArgs:function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Negative=function(a){this.value=a},a.Negative.prototype={type:"Negative",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("-"),this.value.genCSS(a,b)},toCSS:a.toCSS,eval:function(b){return b.isMathOn()?new a.Operation("*",[new a.Dimension(-1),this.value]).eval(b):new a.Negative(this.value.eval(b))}}}(c("../tree")),function(a){a.Operation=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c},a.Operation.prototype={type:"Operation",accept:function(a){this.operands=a.visit(this.operands)},eval:function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b);if(b.isMathOn()){if(c instanceof a.Dimension&&d instanceof a.Color&&(c=c.toColor()),d instanceof a.Dimension&&c instanceof a.Color&&(d=d.toColor()),!c.operate)throw{type:"Operation",message:"Operation on an invalid type"};return c.operate(b,this.op,d)}return new a.Operation(this.op,[c,d],this.isSpaced)},genCSS:function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},toCSS:a.toCSS},a.operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={type:"Paren",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},toCSS:a.toCSS,eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e},a.Quoted.prototype={type:"Quoted",genCSS:function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},toCSS:a.toCSS,eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return new a.JavaScript(e,c.index,!0).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=new a.Variable("@"+e,c.index,c.currentFileInfo).eval(b,!0);return f instanceof a.Quoted?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},compare:function(a){if(!a.toCSS)return-1;var b,c;return"Quoted"!==a.type||this.escaped||a.escaped?(b=this.toCSS(),c=a.toCSS()):(b=a.value,c=this.value),b===c?0:c>b?-1:1}}}(c("../tree")),function(a){function c(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}a.Rule=function(c,d,e,f,g,h,i,j){this.name=c,this.value=d instanceof a.Value||d instanceof a.Ruleset?d:new a.Value([d]),this.important=e?" "+e.trim():"",this.merge=f,this.index=g,this.currentFileInfo=h,this.inline=i||!1,this.variable=j!==b?j:c.charAt&&"@"===c.charAt(0)},a.Rule.prototype={type:"Rule",accept:function(a){this.value=a.visit(this.value)},genCSS:function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},toCSS:a.toCSS,eval:function(b){var d,e=!1,f=this.name,g=this.variable;"string"!=typeof f&&(f=1===f.length&&f[0]instanceof a.Keyword?f[0].value:c(b,f),g=!1),"font"!==f||b.strictMath||(e=!0,b.strictMath=!0);try{if(d=this.value.eval(b),!this.variable&&"DetachedRuleset"===d.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new a.Rule(f,d,this.important,this.merge,this.index,this.currentFileInfo,this.inline,g)}catch(h){throw"number"!=typeof h.index&&(h.index=this.index,h.filename=this.currentFileInfo.filename),h}finally{e&&(b.strictMath=!1)}},makeImportant:function(){return new a.Rule(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)}}}(c("../tree")),function(a){a.RulesetCall=function(a){this.variable=a},a.RulesetCall.prototype={type:"RulesetCall",accept:function(){},eval:function(b){var c=new a.Variable(this.variable).eval(b);return c.callEval(b)}}}(c("../tree")),function(a){a.Ruleset=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c},a.Ruleset.prototype={type:"Ruleset",accept:function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},eval:function(b){var c,d,e,f,g=this.selectors,h=a.defaultFunc,i=!1;if(g&&(d=g.length)){for(c=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;d>f;f++)e=g[f].eval(b),c.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var j,k,l=this.rules?this.rules.slice(0):null,m=new a.Ruleset(c,l,this.strictImports);m.originalRuleset=this,m.root=this.root,m.firstRoot=this.firstRoot,m.allowImports=this.allowImports,this.debugInfo&&(m.debugInfo=this.debugInfo),i||(l.length=0);var n=b.frames;n.unshift(m);var o=b.selectors;o||(b.selectors=o=[]),o.unshift(this.selectors),(m.root||m.allowImports||!m.strictImports)&&m.evalImports(b);var p=m.rules,q=p?p.length:0;for(f=0;q>f;f++)(p[f]instanceof a.mixin.Definition||p[f]instanceof a.DetachedRuleset)&&(p[f]=p[f].eval(b));var r=b.mediaBlocks&&b.mediaBlocks.length||0;for(f=0;q>f;f++)p[f]instanceof a.mixin.Call?(l=p[f].eval(b).filter(function(b){return b instanceof a.Rule&&b.variable?!m.variable(b.name):!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache()):p[f]instanceof a.RulesetCall&&(l=p[f].eval(b).rules.filter(function(b){return b instanceof a.Rule&&b.variable?!1:!0}),p.splice.apply(p,[f,1].concat(l)),q+=l.length-1,f+=l.length-1,m.resetCache());for(f=0;fb;b++)c=g[b],(c instanceof d||c instanceof e)&&f.push(c);return f},prependRule:function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},find:function(b,c){c=c||this;var d,e=[],f=b.toCSS();return f in this._lookups?this._lookups[f]:(this.rulesets().forEach(function(f){if(f!==c)for(var g=0;gd?Array.prototype.push.apply(e,f.find(new a.Selector(b.elements.slice(d)),c)):e.push(f);break}}),this._lookups[f]=e,e)},genCSS:function(b,c){function d(b,c){return b.rules?!0:b instanceof a.Media||c&&b instanceof a.Comment?!0:b instanceof a.Directive||b instanceof a.Anonymous?b.isRulesetLike():!1}var e,f,g,h,i,j,k=[],l=[],m=[];b.tabLevel=b.tabLevel||0,this.root||b.tabLevel++;var n,o=b.compress?"":Array(b.tabLevel+1).join(" "),p=b.compress?"":Array(b.tabLevel).join(" ");for(e=0;ee;e++)if(j=r[e],q=j.length)for(e>0&&c.add(n),b.firstSelector=!0,j[0].genCSS(b,c),b.firstSelector=!1,f=1;q>f;f++)j[f].genCSS(b,c);c.add((b.compress?"{":" {\n")+o)}for(e=0;ee;e++)n&&c.add(n),m[e].genCSS(b,c);c.isEmpty()||b.compress||!this.firstRoot||c.add("\n")},toCSS:a.toCSS,markReferenced:function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),f=0;f0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new a.Element(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=d.createDerived(q.elements.slice(0)),p=!1):o=d.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0&&(p=!1,o.elements.push(new a.Element(j.combinator,l[0].elements[0].value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))),p||m.push(o),m=m.concat(n),s.push(m);i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&b.push(i[e])}else if(c.length>0)for(e=0;e0?e[e.length-1]=e[e.length-1].createDerived(e[e.length-1].elements.concat(b)):e.push(new a.Selector(b))}}}(c("../tree")),function(a){a.Selector=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)},a.Selector.prototype={type:"Selector",accept:function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},createDerived:function(b,c,d){d=null!=d?d:this.evaldCondition;var e=new a.Selector(b,c||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=d,e.mediaEmpty=this.mediaEmpty,e},match:function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},CacheElements:function(){var a,b,c,d="";if(!this._elements){for(a=this.elements.length,c=0;a>c;c++)if(b=this.elements[c],d+=b.combinator.value,b.value.value){if("string"!=typeof b.value.value){d="";break}d+=b.value.value}else d+=b.value;this._elements=d.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g),this._elements?"&"===this._elements[0]&&this._elements.shift():this._elements=[]}},isJustParentSelector:function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},eval:function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},genCSS:function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;cc;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}}}(c("./tree")),function(a){a.importVisitor=function(b,c,d,e,f){if(this._visitor=new a.visitor(this),this._importer=b,this._finish=c,this.env=d||new a.evalEnv,this.importCount=0,this.onceFileDetectionMap=e||{},this.recursionDetector={},f)for(var g in f)f.hasOwnProperty(g)&&(this.recursionDetector[g]=!0)},a.importVisitor.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(b,c){var d,e=this,f=b.options.inline;if(!b.css||f){try{d=b.evalForImport(this.env)}catch(g){g.filename||(g.index=b.index,g.filename=b.currentFileInfo.filename),b.css=!0,b.error=g}if(d&&(!d.css||f)){b=d,this.importCount++;var h=new a.evalEnv(this.env,this.env.frames.slice(0));b.options.multiple&&(h.importMultiple=!0),this._importer.push(b.getPath(),b.currentFileInfo,b.options,function(c,d,g,i){c&&!c.filename&&(c.index=b.index,c.filename=b.currentFileInfo.filename);var j=g||i in e.recursionDetector;h.importMultiple||(b.skip=j?!0:function(){return i in e.onceFileDetectionMap?!0:(e.onceFileDetectionMap[i]=!0,!1)});var k=function(a){e.importCount--,0===e.importCount&&e.isFinished&&e._finish(a)};return!d||(b.root=d,b.importedFilename=i,f||!h.importMultiple&&j)?void k():(e.recursionDetector[i]=!0,void new a.importVisitor(e._importer,k,h,e.onceFileDetectionMap,e.recursionDetector).run(d))})}}return c.visitDeeper=!1,b},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.env.frames.unshift(a),a},visitDirectiveOut:function(){this.env.frames.shift()},visitMixinDefinition:function(a){return this.env.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.env.frames.shift()},visitRuleset:function(a){return this.env.frames.unshift(a),a},visitRulesetOut:function(){this.env.frames.shift()},visitMedia:function(a){return this.env.frames.unshift(a.rules[0]),a},visitMediaOut:function(){this.env.frames.shift()}}}(c("./tree")),function(a){a.joinSelectorVisitor=function(){this.contexts=[[]],this._visitor=new a.visitor(this)},a.joinSelectorVisitor.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}}}(c("./tree")),function(a){a.toCSSVisitor=function(b){this._visitor=new a.visitor(this),this._env=b},a.toCSSVisitor.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._env)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(b){if(b.currentFileInfo.reference&&!b.isReferenced)return[];if("@charset"===b.name){if(this.charset){if(b.debugInfo){var c=new a.Comment("/* "+b.toCSS(this._env).replace(/\n/g,"")+" */\n");return c.debugInfo=b.debugInfo,this._visitor.visit(c)}return[]}this.charset=!0}return b.rules&&b.rules.rules&&this._mergeRules(b.rules.rules),b},checkPropertiesInRoot:function(b){for(var c,d=0;d0)&&e.splice(0,0,b);else{b.paths&&(b.paths=b.paths.filter(function(b){var c;for(" "===b[0].elements[0].combinator.value&&(b[0].elements[0].combinator=new a.Combinator("")),c=0;ch;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?b.accept(this._visitor):b.rules=null,c.visitDeeper=!1,f=b.rules,f&&(this._mergeRules(f),f=b.rules),f&&(this._removeDuplicateRules(f),f=b.rules),f&&f.length>0&&b.paths.length>0&&e.splice(0,0,b)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(b){if(b){var c,d,e,f={};for(e=b.length-1;e>=0;e--)if(d=b[e],d instanceof a.Rule)if(f[d.name]){c=f[d.name],c instanceof a.Rule&&(c=f[d.name]=[f[d.name].toCSS(this._env)]);var g=d.toCSS(this._env);-1!==c.indexOf(g)?b.splice(e,1):c.push(g)}else f[d.name]=d}},_mergeRules:function(b){if(b){for(var c,d,e,f={},g=0;g1){d=c[0];var h=[],i=[];c.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}}}(c("./tree")),function(a){a.extendFinderVisitor=function(){this._visitor=new a.visitor(this),this.contexts=[],this.allExtendsStack=[[]]},a.extendFinderVisitor.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(b){if(!b.root){var c,d,e,f,g=[],h=b.rules,i=h?h.length:0;for(c=0;i>c;c++)b.rules[c]instanceof a.Extend&&(g.push(h[c]),b.extendOnEveryPath=!0);var j=b.paths;for(c=0;c=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(b){h=n.extendSelector(g,i,b),l=new a.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,c,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(c.slice(j,h.pathIndex)),l.push(new a.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=c[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(c[j].elements.slice(k)),j++),l=l.concat(c.slice(j,c.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}}}(c("./tree")),function(a){a.sourceMapOutput=function(a){this._css=[],this._rootNode=a.rootNode,this._writeSourceMap=a.writeSourceMap,this._contentsMap=a.contentsMap,this._contentsIgnoredCharsMap=a.contentsIgnoredCharsMap,this._sourceMapFilename=a.sourceMapFilename,this._outputFilename=a.outputFilename,this._sourceMapURL=a.sourceMapURL,a.sourceMapBasepath&&(this._sourceMapBasepath=a.sourceMapBasepath.replace(/\\/g,"/")),this._sourceMapRootpath=a.sourceMapRootpath,this._outputSourceFiles=a.outputSourceFiles,this._sourceMapGeneratorConstructor=a.sourceMapGenerator||c("source-map").SourceMapGenerator,this._sourceMapRootpath&&"/"!==this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1)&&(this._sourceMapRootpath+="/"),this._lineNumber=0,this._column=0},a.sourceMapOutput.prototype.normalizeFilename=function(a){return a=a.replace(/\\/g,"/"),this._sourceMapBasepath&&0===a.indexOf(this._sourceMapBasepath)&&(a=a.substring(this._sourceMapBasepath.length),("\\"===a.charAt(0)||"/"===a.charAt(0))&&(a=a.substring(1))),(this._sourceMapRootpath||"")+a},a.sourceMapOutput.prototype.add=function(a,b,c,d){if(a){var e,f,g,h,i;if(b){var j=this._contentsMap[b.filename];this._contentsIgnoredCharsMap[b.filename]&&(c-=this._contentsIgnoredCharsMap[b.filename],0>c&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var e,f=JSON.stringify(this._sourceMapGenerator.toJSON());this._sourceMapURL?e=this._sourceMapURL:this._sourceMapFilename&&(e=this.normalizeFilename(this._sourceMapFilename)),this._writeSourceMap?this._writeSourceMap(f):e="data:application/json;base64,"+c("./encoder.js").encodeBase64(f),e&&this._css.push("/*# sourceMappingURL="+e+" */")}return this._css.join("")}}(c("./tree"));var y=/^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);w.env=w.env||("127.0.0.1"==location.hostname||"0.0.0.0"==location.hostname||"localhost"==location.hostname||location.port&&location.port.length>0||y?"development":"production");var z={debug:3,info:2,errors:1,none:0};if(w.logLevel="undefined"!=typeof w.logLevel?w.logLevel:"development"===w.env?z.debug:z.errors,w.async=w.async||!1,w.fileAsync=w.fileAsync||!1,w.poll=w.poll||(y?1e3:1500),w.functions)for(var A in w.functions)w.functions.hasOwnProperty(A)&&(w.tree.functions[A]=w.functions[A]);var B=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);B&&(w.dumpLineNumbers=B[1]);var C=/^text\/(x-)?less$/,D=null,E={};if(w.watch=function(){return w.watchMode||(w.env="development",v()),this.watchMode=!0,!0},w.unwatch=function(){return clearInterval(w.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&w.watch(),"development"!=w.env)try{D="undefined"==typeof a.localStorage?null:a.localStorage}catch(F){}var G=document.getElementsByTagName("link");w.sheets=[];for(var H=0;H */ - -// -// Stub out `require` in rhino -// -function require(arg) { - var split = arg.split('/'); - var resultModule = split.length == 1 ? less.modules[split[0]] : less[split[1]]; - if (!resultModule) { - throw { message: "Cannot find module '" + arg + "'"}; - } - return resultModule; -} - - -if (typeof(window) === 'undefined') { less = {} } -else { less = window.less = {} } -tree = less.tree = {}; -less.mode = 'rhino'; - -(function() { - - console = function() { - var stdout = java.lang.System.out; - var stderr = java.lang.System.err; - - function doLog(out, type) { - return function() { - var args = java.lang.reflect.Array.newInstance(java.lang.Object, arguments.length - 1); - var format = arguments[0]; - var conversionIndex = 0; - // need to look for %d (integer) conversions because in Javascript all numbers are doubles - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (conversionIndex != -1) { - conversionIndex = format.indexOf('%', conversionIndex); - } - if (conversionIndex >= 0 && conversionIndex < format.length) { - var conversion = format.charAt(conversionIndex + 1); - if (conversion === 'd' && typeof arg === 'number') { - arg = new java.lang.Integer(new java.lang.Double(arg).intValue()); - } - conversionIndex++; - } - args[i-1] = arg; - } - try { - out.println(type + java.lang.String.format(format, args)); - } catch(ex) { - stderr.println(ex); - } - } - } - return { - log: doLog(stdout, ''), - info: doLog(stdout, 'INFO: '), - error: doLog(stderr, 'ERROR: '), - warn: doLog(stderr, 'WARN: ') - }; - }(); - - less.modules = {}; - - less.modules.path = { - join: function() { - var parts = []; - for (i in arguments) { - parts = parts.concat(arguments[i].split(/\/|\\/)); - } - var result = []; - for (i in parts) { - var part = parts[i]; - if (part === '..' && result.length > 0 && result[result.length-1] !== '..') { - result.pop(); - } else if (part === '' && result.length > 0) { - // skip - } else if (part !== '.') { - if (part.slice(-1)==='\\' || part.slice(-1)==='/') { - part = part.slice(0, -1); - } - result.push(part); - } - } - return result.join('/'); - }, - dirname: function(p) { - var path = p.split('/'); - path.pop(); - return path.join('/'); - }, - basename: function(p, ext) { - var base = p.split('/').pop(); - if (ext) { - var index = base.lastIndexOf(ext); - if (base.length === index + ext.length) { - base = base.substr(0, index); - } - } - return base; - }, - extname: function(p) { - var index = p.lastIndexOf('.'); - return index > 0 ? p.substring(index) : ''; - } - }; - - less.modules.fs = { - readFileSync: function(name) { - // read a file into a byte array - var file = new java.io.File(name); - var stream = new java.io.FileInputStream(file); - var buffer = []; - var c; - while ((c = stream.read()) != -1) { - buffer.push(c); - } - stream.close(); - return { - length: buffer.length, - toString: function(enc) { - if (enc === 'base64') { - return encodeBase64Bytes(buffer); - } else if (enc) { - return java.lang.String["(byte[],java.lang.String)"](buffer, enc); - } else { - return java.lang.String["(byte[])"](buffer); - } - } - }; - } - }; - - less.encoder = { - encodeBase64: function(str) { - return encodeBase64String(str); - } - }; - - // --------------------------------------------------------------------------------------------- - // private helper functions - // --------------------------------------------------------------------------------------------- - - function encodeBase64Bytes(bytes) { - // requires at least a JRE Platform 6 (or JAXB 1.0 on the classpath) - return javax.xml.bind.DatatypeConverter.printBase64Binary(bytes) - } - function encodeBase64String(str) { - return encodeBase64Bytes(new java.lang.String(str).getBytes()); - } - -})(); - -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} -// -// less.js - parser -// -// A relatively straight-forward predictive parser. -// There is no tokenization/lexing stage, the input is parsed -// in one sweep. -// -// To make the parser fast enough to run in the browser, several -// optimization had to be made: -// -// - Matching and slicing on a huge input is often cause of slowdowns. -// The solution is to chunkify the input into smaller strings. -// The chunks are stored in the `chunks` var, -// `j` holds the current chunk index, and `currentPos` holds -// the index of the current chunk in relation to `input`. -// This gives us an almost 4x speed-up. -// -// - In many cases, we don't need to match individual tokens; -// for example, if a value doesn't hold any variables, operations -// or dynamic references, the parser can effectively 'skip' it, -// treating it as a literal. -// An example would be '1px solid #000' - which evaluates to itself, -// we don't need to know what the individual components are. -// The drawback, of course is that you don't get the benefits of -// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, -// and a smaller speed-up in the code-gen. -// -// -// Token matching is done with the `$` function, which either takes -// a terminal string or regexp, or a non-terminal function to call. -// It also takes care of moving all the indices forwards. -// -// -less.Parser = function Parser(env) { - var input, // LeSS input string - i, // current index in `input` - j, // current chunk - saveStack = [], // holds state for backtracking - furthest, // furthest index the parser has gone to - chunks, // chunkified input - current, // current chunk - currentPos, // index of current chunk, in `input` - parser, - parsers, - rootFilename = env && env.filename; - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if (!(env instanceof tree.parseEnv)) { - env = new tree.parseEnv(env); - } - - var imports = this.imports = { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); - - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - - var importedPreviously = fullPath === rootFilename; - - parserImports.files[fullPath] = root; // Store the root - - if (e && !parserImports.error) { parserImports.error = e; } - - callback(e, root, importedPreviously, fullPath); - }; - - if (less.Parser.importer) { - less.Parser.importer(path, currentFileInfo, fileParsedFunc, env); - } else { - less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) { - if (e) {fileParsedFunc(e); return;} - - var newEnv = new tree.parseEnv(env); - - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[fullPath] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, fullPath); - } else { - new(less.Parser)(newEnv).parse(contents, function (e, root) { - fileParsedFunc(e, root, fullPath); - }); - } - }, env); - } - } - }; - - function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); } - function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; } - function forget() { saveStack.pop(); } - - function sync() { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - } - function isWhitespace(str, pos) { - var code = str.charCodeAt(pos | 0); - return (code <= 32) && (code === 32 || code === 10 || code === 9); - } - // - // Parse from a token, regexp or string, and move forward if match - // - function $(tok) { - var tokType = typeof tok, - match, length; - - // Either match a single character in the input, - // or match a regexp in the current chunk (`current`). - // - if (tokType === "string") { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - // regexp - sync (); - if (! (match = tok.exec(current))) { - return null; - } - - length = match[0].length; - - // The match is confirmed, add the match length to `i`, - // 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. - // - skipWhitespace(length); - - if(typeof(match) === 'string') { - return match; - } else { - return match.length === 1 ? match[0] : match; - } - } - - // Specialization of $(tok) - function $re(tok) { - if (i > currentPos) { - current = current.slice(i - currentPos); - currentPos = i; - } - var m = tok.exec(current); - if (!m) { - return null; - } - - skipWhitespace(m[0].length); - if(typeof m === "string") { - return m; - } - - return m.length === 1 ? m[0] : m; - } - - var _$re = $re; - - // Specialization of $(tok) - function $char(tok) { - if (input.charAt(i) !== tok) { - return null; - } - skipWhitespace(1); - return tok; - } - - function skipWhitespace(length) { - var oldi = i, oldj = j, - curr = i - currentPos, - endIndex = i + current.length - curr, - mem = (i += length), - inp = input, - c; - - for (; i < endIndex; i++) { - c = inp.charCodeAt(i); - if (c > 32) { - break; - } - - if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) { - break; - } - } - - current = current.slice(length + i - mem + curr); - currentPos = i; - - if (!current.length && (j < chunks.length - 1)) { - current = chunks[++j]; - skipWhitespace(0); // skip space at the beginning of a chunk - return true; // things changed - } - - return oldi !== i || oldj !== j; - } - - function expect(arg, msg, index) { - // some older browsers return typeof 'function' for RegExp - var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg); - if (result) { - return result; - } - error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'" - : "unexpected token")); - } - - // Specialization of expect() - function expectChar(arg, msg) { - if (input.charAt(i) === arg) { - skipWhitespace(1); - return arg; - } - error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'"); - } - - function error(msg, type) { - var e = new Error(msg); - e.index = i; - e.type = type || 'Syntax'; - throw e; - } - - // Same as $(), but don't change the state of the parser, - // just return the match. - function peek(tok) { - if (typeof(tok) === 'string') { - return input.charAt(i) === tok; - } else { - return tok.test(current); - } - } - - // Specialization of peek() - function peekChar(tok) { - return input.charAt(i) === tok; - } - - - function getInput(e, env) { - if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) { - return parser.imports.contents[e.filename]; - } else { - return input; - } - } - - function getLocation(index, inputStream) { - var n = index + 1, - line = null, - column = -1; - - while (--n >= 0 && inputStream.charAt(n) !== '\n') { - column++; - } - - if (typeof index === 'number') { - line = (inputStream.slice(0, index).match(/\n/g) || "").length; - } - - return { - line: line, - column: column - }; - } - - function getDebugInfo(index, inputStream, env) { - var filename = env.currentFileInfo.filename; - if(less.mode !== 'browser' && less.mode !== 'rhino') { - filename = require('path').resolve(filename); - } - - return { - lineNumber: getLocation(index, inputStream).line + 1, - fileName: filename - }; - } - - function LessError(e, env) { - var input = getInput(e, env), - loc = getLocation(e.index, input), - line = loc.line, - col = loc.column, - callLine = e.call && getLocation(e.call, input).line, - lines = input.split('\n'); - - this.type = e.type || 'Syntax'; - this.message = e.message; - this.filename = e.filename || env.currentFileInfo.filename; - this.index = e.index; - this.line = typeof(line) === 'number' ? line + 1 : null; - this.callLine = callLine + 1; - this.callExtract = lines[callLine]; - this.stack = e.stack; - this.column = col; - this.extract = [ - lines[line - 1], - lines[line], - lines[line + 1] - ]; - } - - LessError.prototype = new Error(); - LessError.prototype.constructor = LessError; - - this.env = env = env || {}; - - // The optimization level dictates the thoroughness of the parser, - // the lower the number, the less nodes it will create in the tree. - // This could matter for debugging, or if you want to access - // the individual nodes in the tree. - this.optimization = ('optimization' in this.env) ? this.env.optimization : 1; - - // - // The Parser - // - parser = { - - imports: imports, - // - // Parse an input string into an abstract syntax tree, - // @param str A string containing 'less' markup - // @param callback call `callback` when done. - // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply - // - parse: function (str, callback, additionalData) { - var root, line, lines, error = null, globalVars, modifyVars, preText = ""; - - i = j = currentPos = furthest = 0; - - globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : ''; - modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : ''; - - if (globalVars || (additionalData && additionalData.banner)) { - preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; - } - - str = str.replace(/\r\n/g, '\n'); - // Remove potential UTF Byte Order Mark - input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - parser.imports.contents[env.currentFileInfo.filename] = str; - - // Split the input into chunks. - chunks = (function (input) { - var len = input.length, level = 0, parenLevel = 0, - lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, - chunks = [], emitFrom = 0, - parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched; - - function fail(msg, index) { - error = new(LessError)({ - index: index || parserCurrentIndex, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); - } - - function emitChunk(force) { - var len = parserCurrentIndex - emitFrom; - if (((len < 512) && !force) || !len) { - return; - } - chunks.push(input.slice(emitFrom, parserCurrentIndex + 1)); - emitFrom = parserCurrentIndex + 1; - } - - for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) { - cc = input.charCodeAt(parserCurrentIndex); - if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { - // a-z or whitespace - continue; - } - - switch (cc) { - case 40: // ( - parenLevel++; - lastOpeningParen = parserCurrentIndex; - continue; - case 41: // ) - if (--parenLevel < 0) { - return fail("missing opening `(`"); - } - continue; - case 59: // ; - if (!parenLevel) { emitChunk(); } - continue; - case 123: // { - level++; - lastOpening = parserCurrentIndex; - continue; - case 125: // } - if (--level < 0) { - return fail("missing opening `{`"); - } - if (!level && !parenLevel) { emitChunk(); } - continue; - case 92: // \ - if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; } - return fail("unescaped `\\`"); - case 34: - case 39: - case 96: // ", ' and ` - matched = 0; - currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 > 96) { continue; } - if (cc2 == cc) { matched = 1; break; } - if (cc2 == 92) { // \ - if (parserCurrentIndex == len - 1) { - return fail("unescaped `\\`"); - } - parserCurrentIndex++; - } - } - if (matched) { continue; } - return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); - case 47: // /, check for comment - if (parenLevel || (parserCurrentIndex == len - 1)) { continue; } - cc2 = input.charCodeAt(parserCurrentIndex + 1); - if (cc2 == 47) { - // //, find lnfeed - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } - } - } else if (cc2 == 42) { - // /*, find */ - lastMultiComment = currentChunkStartIndex = parserCurrentIndex; - for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) { - cc2 = input.charCodeAt(parserCurrentIndex); - if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; } - if (cc2 != 42) { continue; } - if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; } - } - if (parserCurrentIndex == len - 1) { - return fail("missing closing `*/`", currentChunkStartIndex); - } - parserCurrentIndex++; - } - continue; - case 42: // *, check for unmatched */ - if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) { - return fail("unmatched `/*`"); - } - continue; - } - } - - if (level !== 0) { - if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { - return fail("missing closing `}` or `*/`", lastOpening); - } else { - return fail("missing closing `}`", lastOpening); - } - } else if (parenLevel !== 0) { - return fail("missing closing `)`", lastOpeningParen); - } - - emitChunk(true); - return chunks; - })(str); - - if (error) { - return callback(new(LessError)(error, env)); - } - - current = chunks[0]; - - // Start with the primary rule. - // The whole syntax tree is held under a Ruleset node, - // with the `root` property set to true, so no `{}` are - // output. The callback is called when the input is parsed. - try { - root = new(tree.Ruleset)(null, this.parsers.primary()); - root.root = true; - root.firstRoot = true; - } catch (e) { - return callback(new(LessError)(e, env)); - } - - root.toCSS = (function (evaluate) { - return function (options, variables) { - options = options || {}; - var evaldRoot, - css, - evalEnv = new tree.evalEnv(options); - - // - // Allows setting variables with a hash, so: - // - // `{ color: new(tree.Color)('#f01') }` will become: - // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') - // ]) - // ]) - // ) - // - if (typeof(variables) === 'object' && !Array.isArray(variables)) { - variables = Object.keys(variables).map(function (k) { - var value = variables[k]; - - if (! (value instanceof tree.Value)) { - if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); - } - value = new(tree.Value)([value]); - } - return new(tree.Rule)('@' + k, value, false, null, 0); - }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; - } - - try { - var preEvalVisitors = [], - visitors = [ - new(tree.joinSelectorVisitor)(), - new(tree.processExtendsVisitor)(), - new(tree.toCSSVisitor)({compress: Boolean(options.compress)}) - ], i, root = this; - - if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); - } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); - } else { - visitors.push(options.plugins[i]); - } - } - } - } - - for(i = 0; i < preEvalVisitors.length; i++) { - preEvalVisitors[i].run(root); - } - - evaldRoot = evaluate.call(root, evalEnv); - - for(i = 0; i < visitors.length; i++) { - visitors[i].run(evaldRoot); - } - - if (options.sourceMap) { - evaldRoot = new tree.sourceMapOutput( - { - contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: parser.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ - compress: Boolean(options.compress), - dumpLineNumbers: env.dumpLineNumbers, - strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); - } catch (e) { - throw new(LessError)(e, env); - } - - if (options.cleancss && less.mode === 'node') { - var CleanCSS = require('clean-css'), - cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - return new CleanCSS(cleancssOptions).minify(css); - } else if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; - } - }; - })(root.eval); - - // If `i` is smaller than the `input.length - 1`, - // it means the parser wasn't able to parse the whole - // string, so we've got a parsing error. - // - // We try to extract a \n delimited string, - // showing the line where the parse error occured. - // We split it up into two parts (the part which parsed, - // and the part which didn't), so we can color them differently. - if (i < input.length - 1) { - i = furthest; - var loc = getLocation(i, input); - lines = input.split('\n'); - line = loc.line + 1; - - error = { - type: "Parse", - message: "Unrecognised input", - index: i, - filename: env.currentFileInfo.filename, - line: line, - column: loc.column, - extract: [ - lines[line - 2], - lines[line - 1], - lines[line] - ] - }; - } - - var finish = function (e) { - e = error || e || parser.imports.error; - - if (e) { - if (!(e instanceof LessError)) { - e = new(LessError)(e, env); - } - - return callback(e); - } - else { - return callback(null, root); - } - }; - - if (env.processImports !== false) { - new tree.importVisitor(this.imports, finish) - .run(root); - } else { - return finish(); - } - }, - - // - // 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()`. - // - parsers: parsers = { - // - // 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. - // - primary: function () { - var mixin = this.mixin, $re = _$re, root = [], node; - - while (current) - { - node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || - mixin.call() || this.comment() || this.rulesetCall() || this.directive(); - if (node) { - root.push(node); - } else { - if (!($re(/^[\s\n]+/) || $re(/^;+/))) { - break; - } - } - if (peekChar('}')) { - break; - } - } - - return root; - }, - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - comment: function () { - var comment; - - if (input.charAt(i) !== '/') { return; } - - if (input.charAt(i + 1) === '/') { - return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo); - } - comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/); - if (comment) { - return new(tree.Comment)(comment, false, i, env.currentFileInfo); - } - }, - - comments: function () { - var comment, comments = []; - - while(true) { - comment = this.comment(); - if (!comment) { - break; - } - comments.push(comment); - } - - return comments; - }, - - // - // Entities are tokens which can be found inside an Expression - // - entities: { - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - quoted: function () { - var str, j = i, e, index = i; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; } - - if (e) { $char('~'); } - - str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/); - if (str) { - return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo); - } - }, - - // - // A catch-all word, such as: - // - // black border-collapse - // - keyword: function () { - var k; - - k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); - if (k) { - var color = tree.Color.fromKeyword(k); - if (color) { - return color; - } - return new(tree.Keyword)(k); - } - }, - - // - // 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. - // - call: function () { - var name, nameLC, args, alpha_ret, index = i; - - name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current); - if (!name) { return; } - - name = name[1]; - nameLC = name.toLowerCase(); - if (nameLC === 'url') { - return null; - } - - i += name.length; - - if (nameLC === 'alpha') { - alpha_ret = parsers.alpha(); - if(typeof alpha_ret !== 'undefined') { - return alpha_ret; - } - } - - $char('('); // Parse the '(' and consume whitespace. - - args = this.arguments(); - - if (! $char(')')) { - return; - } - - if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); } - }, - arguments: function () { - var args = [], arg; - - while (true) { - arg = this.assignment() || parsers.expression(); - if (!arg) { - break; - } - args.push(arg); - if (! $char(',')) { - break; - } - } - return args; - }, - literal: function () { - return this.dimension() || - this.color() || - this.quoted() || - this.unicodeDescriptor(); - }, - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - - assignment: function () { - var key, value; - key = $re(/^\w+(?=\s?=)/i); - if (!key) { - return; - } - if (!$char('=')) { - return; - } - value = parsers.entity(); - if (value) { - return new(tree.Assignment)(key, 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. - // - url: function () { - var value; - - if (input.charAt(i) !== 'u' || !$re(/^url\(/)) { - return; - } - - value = this.quoted() || this.variable() || - $re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; - - expectChar(')'); - - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); - }, - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - variable: function () { - var name, index = i; - - if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); - } - }, - - // A variable entity useing the protective {} e.g. @{var} - variableCurly: function () { - var curly, index = i; - - if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); - } - }, - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - color: function () { - var rgb; - - if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { - var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string - colorCandidateString = colorCandidateString[1]; - if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters - error("Invalid HEX color code"); - } - return new(tree.Color)(rgb[1]); - } - }, - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - dimension: function () { - var value, c = input.charCodeAt(i); - //Is the first char of the dimension 0-9, '.', '+' or '-' - if ((c > 57 || c < 43) || c === 47 || c == 44) { - return; - } - - value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/); - if (value) { - return new(tree.Dimension)(value[1], value[2]); - } - }, - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - unicodeDescriptor: function () { - var ud; - - ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); - if (ud) { - return new(tree.UnicodeDescriptor)(ud[0]); - } - }, - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - javascript: function () { - var str, j = i, e; - - if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings - if (input.charAt(j) !== '`') { return; } - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { - error("You are using JavaScript, which has been disabled."); - } - - if (e) { $char('~'); } - - str = $re(/^`([^`]*)`/); - if (str) { - return new(tree.JavaScript)(str[1], i, e); - } - } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - variable: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; } - }, - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - rulesetCall: function () { - var name; - - if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { - return new tree.RulesetCall(name[1]); - } - }, - - // - // extend syntax - used to extend selectors - // - extend: function(isRule) { - var elements, e, index = i, option, extendList, extend; - - if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; } - - do { - option = null; - elements = null; - while (! (option = $re(/^(all)(?=\s*(\)|,))/))) { - e = this.element(); - if (!e) { break; } - if (elements) { elements.push(e); } else { elements = [ e ]; } - } - - option = option && option[1]; - if (!elements) - error("Missing target selector for :extend()."); - extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - - } while($char(",")); - - expect(/^\)/); - - if (isRule) { - expect(/^;/); - } - - return extendList; - }, - - // - // extendRule - used in a rule to extend all the parent selectors - // - extendRule: function() { - return this.extend(true); - }, - - // - // Mixins - // - mixin: { - // - // 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. - // - call: function () { - var s = input.charAt(i), important = false, index = i, elemIndex, - elements, elem, e, c, args; - - if (s !== '.' && s !== '#') { return; } - - save(); // stop us absorbing part of an invalid selector - - while (true) { - elemIndex = i; - e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); - if (!e) { - break; - } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); - if (elements) { elements.push(elem); } else { elements = [ elem ]; } - c = $char('>'); - } - - if (elements) { - if ($char('(')) { - args = this.args(true).args; - expectChar(')'); - } - - if (parsers.important()) { - important = true; - } - - if (parsers.end()) { - forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); - } - } - - restore(); - }, - args: function (isCall) { - var parsers = parser.parsers, entities = parsers.entities, - returner = { args:null, variadic: false }, - expressions = [], argsSemiColon = [], argsComma = [], - isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; - - save(); - - while (true) { - if (isCall) { - arg = parsers.detachedRuleset() || parsers.expression(); - } else { - parsers.comments(); - if (input.charAt(i) === '.' && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ variadic: true }); - break; - } - arg = entities.variable() || entities.literal() || entities.keyword(); - } - - if (!arg) { - break; - } - - nameLoop = null; - if (arg.throwAwayComments) { - arg.throwAwayComments(); - } - value = arg; - var val = null; - - if (isCall) { - // Variable - if (arg.value && arg.value.length == 1) { - val = arg.value[0]; - } - } else { - val = arg; - } - - if (val && val instanceof tree.Variable) { - if ($char(':')) { - if (expressions.length > 0) { - if (isSemiColonSeperated) { - 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); - - if (!value) { - if (isCall) { - error("could not understand value for named argument"); - } else { - restore(); - returner.args = []; - return returner; - } - } - nameLoop = (name = val.name); - } else if (!isCall && $re(/^\.{3}/)) { - returner.variadic = true; - if ($char(";") && !isSemiColonSeperated) { - isSemiColonSeperated = true; - } - (isSemiColonSeperated ? argsSemiColon : argsComma) - .push({ name: arg.name, variadic: true }); - break; - } else if (!isCall) { - name = nameLoop = val.name; - value = null; - } - } - - if (value) { - expressions.push(value); - } - - argsComma.push({ name:nameLoop, value:value }); - - if ($char(',')) { - continue; - } - - if ($char(';') || isSemiColonSeperated) { - - if (expressionContainsNamed) { - error("Cannot mix ; and , as delimiter types"); - } - - isSemiColonSeperated = true; - - if (expressions.length > 1) { - value = new(tree.Value)(expressions); - } - argsSemiColon.push({ name:name, value:value }); - - name = null; - expressions = []; - expressionContainsNamed = false; - } - } - - 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. - // - definition: function () { - var name, params = [], match, ruleset, cond, variadic = false; - if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || - peek(/^[^{]*\}/)) { - return; - } - - save(); - - match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); - if (match) { - name = match[1]; - - var argInfo = this.args(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 (!$char(')')) { - furthest = i; - restore(); - return; - } - - parsers.comments(); - - if ($re(/^when/)) { // Guard - cond = expect(parsers.conditions, 'expected condition'); - } - - ruleset = parsers.block(); - - if (ruleset) { - forget(); - return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); - } else { - restore(); - } - } else { - forget(); - } - } - }, - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - entity: function () { - var entities = this.entities; - - return entities.literal() || entities.variable() || entities.url() || - entities.call() || entities.keyword() || entities.javascript() || - this.comment(); - }, - - // - // 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. - // - end: function () { - return $char(';') || peekChar('}'); - }, - - // - // IE's alpha function - // - // alpha(opacity=88) - // - alpha: function () { - var value; - - if (! $re(/^\(opacity=/i)) { return; } - value = $re(/^\d+/) || this.entities.variable(); - if (value) { - expectChar(')'); - return new(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 `*`. - // - element: function () { - var e, c, v, index = i; - - c = this.combinator(); - - e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || - $char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) || - this.entities.variableCurly(); - - if (! e) { - save(); - if ($char('(')) { - if ((v = this.selector()) && $char(')')) { - e = new(tree.Paren)(v); - forget(); - } else { - restore(); - } - } else { - forget(); - } - } - - if (e) { return new(tree.Element)(c, e, index, 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. More info on how - // we deal with this in *combinator.js*. - // - combinator: function () { - var c = input.charAt(i); - - if (c === '/') { - save(); - var slashedCombinator = $re(/^\/[a-z]+\//i); - if (slashedCombinator) { - forget(); - return new(tree.Combinator)(slashedCombinator); - } - restore(); - } - - if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { - i++; - if (c === '^' && input.charAt(i) === '^') { - c = '^^'; - i++; - } - while (isWhitespace(input, i)) { i++; } - return new(tree.Combinator)(c); - } else if (isWhitespace(input, i - 1)) { - return new(tree.Combinator)(" "); - } else { - return new(tree.Combinator)(null); - } - }, - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - lessSelector: function () { - return this.selector(true); - }, - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - selector: function (isLess) { - var index = i, $re = _$re, elements, extendList, c, e, extend, when, condition; - - while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) { - if (when) { - condition = expect(this.conditions, 'expected condition'); - } else if (condition) { - error("CSS guard can only be used at the end of selector"); - } else if (extend) { - if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } - } else { - if (extendList) { error("Extend can only be used at the end of selector"); } - c = input.charAt(i); - if (elements) { elements.push(e); } else { elements = [ e ]; } - e = null; - } - if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { - break; - } - } - - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } - if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } - }, - attribute: function () { - if (! $char('[')) { return; } - - var entities = this.entities, - key, val, op; - - if (!(key = entities.variableCurly())) { - key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); - } - - op = $re(/^[|~*$^]?=/); - if (op) { - val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly(); - } - - expectChar(']'); - - return new(tree.Attribute)(key, op, val); - }, - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - block: function () { - var content; - if ($char('{') && (content = this.primary()) && $char('}')) { - return content; - } - }, - - blockRuleset: function() { - var block = this.block(); - - if (block) { - block = new tree.Ruleset(null, block); - } - return block; - }, - - detachedRuleset: function() { - var blockRuleset = this.blockRuleset(); - if (blockRuleset) { - return new tree.DetachedRuleset(blockRuleset); - } - }, - - // - // div, .class, body > p {...} - // - ruleset: function () { - var selectors, s, rules, debugInfo; - - save(); - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - while (true) { - s = this.lessSelector(); - if (!s) { - break; - } - if (selectors) { selectors.push(s); } else { selectors = [ s ]; } - this.comments(); - if (s.condition && selectors.length > 1) { - error("Guards are only currently allowed on a single selector."); - } - if (! $char(',')) { break; } - if (s.condition) { - error("Guards are only currently allowed on a single selector."); - } - this.comments(); - } - - if (selectors && (rules = this.block())) { - forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { - ruleset.debugInfo = debugInfo; - } - return ruleset; - } else { - // Backtrack - furthest = i; - restore(); - } - }, - rule: function (tryAnonymous) { - var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable; - - if (c === '.' || c === '#' || c === '&') { return; } - - save(); - - name = this.variable() || this.ruleProperty(); - if (name) { - isVariable = typeof name === "string"; - - if (isVariable) { - value = this.detachedRuleset(); - } - - this.comments(); - if (!value) { - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - value = !tryAnonymous && (env.compress || isVariable) ? - (this.value() || this.anonymousValue()) : - (this.anonymousValue() || this.value()); - - important = this.important(); - - // 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 - merge = !isVariable && name.pop().value; - } - - if (value && this.end()) { - forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); - } else { - furthest = i; - restore(); - if (value && !tryAnonymous) { - return this.rule(true); - } - } - } else { - forget(); - } - }, - anonymousValue: function () { - var match; - match = /^([^@+\/'"*`(;{}-]*);/.exec(current); - if (match) { - i += match[0].length - 1; - return new(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. - // - "import": function () { - var path, features, index = i; - - var dir = $re(/^@import?\s+/); - - if (dir) { - var options = (dir ? this.importOptions() : null) || {}; - - if ((path = this.entities.quoted() || this.entities.url())) { - features = this.mediaFeatures(); - - if (!$(';')) { - i = index; - error("missing semi-colon or unrecognised media features on import"); - } - features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); - } - else - { - i = index; - error("malformed import statement"); - } - } - }, - - importOptions: function() { - var o, options = {}, optionName, value; - - // list of options, surrounded by parens - if (! $char('(')) { return null; } - do { - o = this.importOption(); - if (o) { - optionName = o; - value = true; - switch(optionName) { - case "css": - optionName = "less"; - value = false; - break; - case "once": - optionName = "multiple"; - value = false; - break; - } - options[optionName] = value; - if (! $char(',')) { break; } - } - } while (o); - expectChar(')'); - return options; - }, - - importOption: function() { - var opt = $re(/^(less|css|multiple|once|inline|reference)/); - if (opt) { - return opt[1]; - } - }, - - mediaFeature: function () { - var entities = this.entities, nodes = [], e, p; - do { - e = entities.keyword() || entities.variable(); - if (e) { - nodes.push(e); - } else if ($char('(')) { - p = this.property(); - e = this.value(); - if ($char(')')) { - if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true))); - } else if (e) { - nodes.push(new(tree.Paren)(e)); - } else { - return null; - } - } else { return null; } - } - } while (e); - - if (nodes.length > 0) { - return new(tree.Expression)(nodes); - } - }, - - mediaFeatures: function () { - var entities = this.entities, features = [], e; - do { - e = this.mediaFeature(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } else { - e = entities.variable(); - if (e) { - features.push(e); - if (! $char(',')) { break; } - } - } - } while (e); - - return features.length > 0 ? features : null; - }, - - media: function () { - var features, rules, media, debugInfo; - - if (env.dumpLineNumbers) { - debugInfo = getDebugInfo(i, input, env); - } - - if ($re(/^@media/)) { - features = this.mediaFeatures(); - - rules = this.block(); - if (rules) { - media = new(tree.Media)(rules, features, i, env.currentFileInfo); - if (env.dumpLineNumbers) { - media.debugInfo = debugInfo; - } - return media; - } - } - }, - - // - // A CSS Directive - // - // @charset "utf-8"; - // - directive: function () { - var index = i, name, value, rules, nonVendorSpecificName, - hasIdentifier, hasExpression, hasUnknown, hasBlock = true; - - if (input.charAt(i) !== '@') { return; } - - value = this['import']() || this.media(); - if (value) { - return value; - } - - save(); - - name = $re(/^@[a-z-]+/); - - if (!name) { return; } - - nonVendorSpecificName = name; - if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { - nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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; - } - - this.comments(); - - if (hasIdentifier) { - value = this.entity(); - if (!value) { - error("expected " + name + " identifier"); - } - } else if (hasExpression) { - value = this.expression(); - if (!value) { - error("expected " + name + " expression"); - } - } else if (hasUnknown) { - value = ($re(/^[^{;]+/) || '').trim(); - if (value) { - value = new(tree.Anonymous)(value); - } - } - - this.comments(); - - if (hasBlock) { - rules = this.blockRuleset(); - } - - if (rules || (!hasBlock && value && $char(';'))) { - forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, - env.dumpLineNumbers ? getDebugInfo(index, input, env) : null); - } - - 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 `;`. - // - value: function () { - var e, expressions = []; - - do { - e = this.expression(); - if (e) { - expressions.push(e); - if (! $char(',')) { break; } - } - } while(e); - - if (expressions.length > 0) { - return new(tree.Value)(expressions); - } - }, - important: function () { - if (input.charAt(i) === '!') { - return $re(/^! *important/); - } - }, - sub: function () { - var a, e; - - if ($char('(')) { - a = this.addition(); - if (a) { - e = new(tree.Expression)([a]); - expectChar(')'); - e.parens = true; - return e; - } - } - }, - multiplication: function () { - var m, a, op, operation, isSpaced; - m = this.operand(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - if (peek(/^\/[*\/]/)) { - break; - } - - save(); - - op = $char('/') || $char('*'); - - if (!op) { forget(); break; } - - a = this.operand(); - - if (!a) { restore(); break; } - forget(); - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - addition: function () { - var m, a, op, operation, isSpaced; - m = this.multiplication(); - if (m) { - isSpaced = isWhitespace(input, i - 1); - while (true) { - op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-'))); - if (!op) { - break; - } - a = this.multiplication(); - if (!a) { - break; - } - - m.parensInOp = true; - a.parensInOp = true; - operation = new(tree.Operation)(op, [operation || m, a], isSpaced); - isSpaced = isWhitespace(input, i - 1); - } - return operation || m; - } - }, - conditions: function () { - var a, b, index = i, condition; - - a = this.condition(); - if (a) { - while (true) { - if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) { - break; - } - b = this.condition(); - if (!b) { - break; - } - condition = new(tree.Condition)('or', condition || a, b, index); - } - return condition || a; - } - }, - condition: function () { - var entities = this.entities, index = i, negate = false, - a, b, c, op; - - if ($re(/^not/)) { negate = true; } - expectChar('('); - a = this.addition() || entities.keyword() || entities.quoted(); - if (a) { - op = $re(/^(?:>=|<=|=<|[<=>])/); - if (op) { - b = this.addition() || entities.keyword() || entities.quoted(); - if (b) { - c = new(tree.Condition)(op, a, b, index, negate); - } else { - error('expected expression'); - } - } else { - c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); - } - expectChar(')'); - return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; - } - }, - - // - // An operand is anything that can be part of an operation, - // such as a Color, or a Variable - // - operand: function () { - var entities = this.entities, - p = input.charAt(i + 1), negate; - - if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); } - var o = this.sub() || entities.dimension() || - entities.color() || entities.variable() || - entities.call(); - - if (negate) { - o.parensInOp = true; - o = new(tree.Negative)(o); - } - - return o; - }, - - // - // Expressions either represent mathematical operations, - // or white-space delimited Entities. - // - // 1px solid black - // @var * 2 - // - expression: function () { - var entities = [], e, delim; - - do { - e = this.addition() || this.entity(); - if (e) { - entities.push(e); - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if (!peek(/^\/[\/*]/)) { - delim = $char('/'); - if (delim) { - entities.push(new(tree.Anonymous)(delim)); - } - } - } - } while (e); - if (entities.length > 0) { - return new(tree.Expression)(entities); - } - }, - property: function () { - var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); - if (name) { - return name[1]; - } - }, - ruleProperty: function () { - var c = current, name = [], index = [], length = 0, s, k; - - function match(re) { - var a = re.exec(c); - if (a) { - index.push(i + length); - length += a[0].length; - c = c.slice(a[1].length); - return name.push(a[1]); - } - } - function cutOutBlockComments() { - //match block comments - var a = /^\s*\/\*(?:[^*]|\*+[^\/*])*\*+\//.exec(c); - if (a) { - length += a[0].length; - c = c.slice(a[0].length); - return true; - } - return false; - } - - match(/^(\*?)/); - while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // ! - while (cutOutBlockComments()); - if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) { - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - skipWhitespace(length); - if (name[0] === '') { - name.shift(); - index.shift(); - } - for (k = 0; k < name.length; k++) { - s = name[k]; - name[k] = (s.charAt(0) !== '@') - ? new(tree.Keyword)(s) - : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); - } - return name; - } - } - } - }; - return parser; -}; -less.Parser.serializeVars = function(vars) { - var s = ''; - - for (var name in vars) { - if (Object.hasOwnProperty.call(vars, name)) { - var value = vars[name]; - s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); - } - } - - return s; -}; - -(function (tree) { - -tree.functions = { - rgb: function (r, g, b) { - return this.rgba(r, g, b, 1.0); - }, - rgba: function (r, g, b, a) { - var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); - a = number(a); - return new(tree.Color)(rgb, a); - }, - hsl: function (h, s, l) { - return this.hsla(h, s, l, 1.0); - }, - hsla: function (h, s, l, a) { - function hue(h) { - 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; } - } - - h = (number(h) % 360) / 360; - s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); - - var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; - var m1 = l * 2 - m2; - - return this.rgba(hue(h + 1/3) * 255, - hue(h) * 255, - hue(h - 1/3) * 255, - a); - }, - - hsv: function(h, s, v) { - return this.hsva(h, s, v, 1.0); - }, - - hsva: function(h, s, v, a) { - h = ((number(h) % 360) / 360) * 360; - s = number(s); v = number(v); a = number(a); - - var i, f; - i = Math.floor((h / 60) % 6); - f = (h / 60) - i; - - var vs = [v, - v * (1 - s), - v * (1 - f * s), - v * (1 - (1 - f) * s)]; - var perm = [[0, 3, 1], - [2, 0, 1], - [1, 0, 3], - [1, 2, 0], - [3, 1, 0], - [0, 1, 2]]; - - return this.rgba(vs[perm[i][0]] * 255, - vs[perm[i][1]] * 255, - vs[perm[i][2]] * 255, - a); - }, - - hue: function (color) { - return new(tree.Dimension)(color.toHSL().h); - }, - saturation: function (color) { - return new(tree.Dimension)(color.toHSL().s * 100, '%'); - }, - lightness: function (color) { - return new(tree.Dimension)(color.toHSL().l * 100, '%'); - }, - hsvhue: function(color) { - return new(tree.Dimension)(color.toHSV().h); - }, - hsvsaturation: function (color) { - return new(tree.Dimension)(color.toHSV().s * 100, '%'); - }, - hsvvalue: function (color) { - return new(tree.Dimension)(color.toHSV().v * 100, '%'); - }, - red: function (color) { - return new(tree.Dimension)(color.rgb[0]); - }, - green: function (color) { - return new(tree.Dimension)(color.rgb[1]); - }, - blue: function (color) { - return new(tree.Dimension)(color.rgb[2]); - }, - alpha: function (color) { - return new(tree.Dimension)(color.toHSL().a); - }, - luma: function (color) { - return new(tree.Dimension)(color.luma() * color.alpha * 100, '%'); - }, - luminance: function (color) { - var luminance = - (0.2126 * color.rgb[0] / 255) - + (0.7152 * color.rgb[1] / 255) - + (0.0722 * color.rgb[2] / 255); - - return new(tree.Dimension)(luminance * color.alpha * 100, '%'); - }, - saturate: function (color, amount) { - // filter: saturate(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - var hsl = color.toHSL(); - - hsl.s += amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - desaturate: function (color, amount) { - var hsl = color.toHSL(); - - hsl.s -= amount.value / 100; - hsl.s = clamp(hsl.s); - return hsla(hsl); - }, - lighten: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l += amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - darken: function (color, amount) { - var hsl = color.toHSL(); - - hsl.l -= amount.value / 100; - hsl.l = clamp(hsl.l); - return hsla(hsl); - }, - fadein: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a += amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fadeout: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a -= amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - fade: function (color, amount) { - var hsl = color.toHSL(); - - hsl.a = amount.value / 100; - hsl.a = clamp(hsl.a); - return hsla(hsl); - }, - spin: function (color, amount) { - var hsl = color.toHSL(); - var hue = (hsl.h + amount.value) % 360; - - hsl.h = hue < 0 ? 360 + hue : hue; - - return hsla(hsl); - }, - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - mix: function (color1, color2, weight) { - if (!weight) { - weight = new(tree.Dimension)(50); - } - var p = weight.value / 100.0; - var w = p * 2 - 1; - var a = color1.toHSL().a - color2.toHSL().a; - - var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; - var w2 = 1 - w1; - - var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, - color1.rgb[1] * w1 + color2.rgb[1] * w2, - color1.rgb[2] * w1 + color2.rgb[2] * w2]; - - var alpha = color1.alpha * p + color2.alpha * (1 - p); - - return new(tree.Color)(rgb, alpha); - }, - greyscale: function (color) { - return this.desaturate(color, new(tree.Dimension)(100)); - }, - contrast: function (color, dark, light, threshold) { - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!color.rgb) { - return null; - } - if (typeof light === 'undefined') { - light = this.rgba(255, 255, 255, 1.0); - } - if (typeof dark === 'undefined') { - dark = this.rgba(0, 0, 0, 1.0); - } - //Figure out which is actually light and dark! - if (dark.luma() > light.luma()) { - var t = light; - light = dark; - dark = t; - } - if (typeof threshold === 'undefined') { - threshold = 0.43; - } else { - threshold = number(threshold); - } - if (color.luma() < threshold) { - return light; - } else { - return dark; - } - }, - e: function (str) { - return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value); - }, - escape: function (str) { - return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); - }, - replace: function (string, pattern, replacement, flags) { - var result = string.value; - - result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - '%': function (string /* arg, arg, ...*/) { - var args = Array.prototype.slice.call(arguments, 1), - result = string.value; - - for (var i = 0; i < args.length; i++) { - /*jshint loopfunc:true */ - result = result.replace(/%[sda]/i, function(token) { - var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); - return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; - }); - } - result = result.replace(/%%/g, '%'); - return new(tree.Quoted)(string.quote || '', result, string.escaped); - }, - unit: function (val, unit) { - if(!(val instanceof tree.Dimension)) { - throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") }; - } - if (unit) { - if (unit instanceof tree.Keyword) { - unit = unit.value; - } else { - unit = unit.toCSS(); - } - } else { - unit = ""; - } - return new(tree.Dimension)(val.value, unit); - }, - convert: function (val, unit) { - return val.convertTo(unit.value); - }, - round: function (n, f) { - var fraction = typeof(f) === "undefined" ? 0 : f.value; - return _math(function(num) { return num.toFixed(fraction); }, null, n); - }, - pi: function () { - return new(tree.Dimension)(Math.PI); - }, - mod: function(a, b) { - return new(tree.Dimension)(a.value % b.value, a.unit); - }, - pow: function(x, y) { - if (typeof x === "number" && typeof y === "number") { - x = new(tree.Dimension)(x); - y = new(tree.Dimension)(y); - } else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) { - throw { type: "Argument", message: "arguments must be numbers" }; - } - - return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit); - }, - _minmax: function (isMin, args) { - args = Array.prototype.slice.call(args); - switch(args.length) { - case 0: throw { type: "Argument", message: "one or more arguments required" }; - } - var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, - order = [], // elems only contains original argument values. - values = {}; // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - for (i = 0; i < args.length; i++) { - current = args[i]; - if (!(current instanceof tree.Dimension)) { - if(Array.isArray(args[i].value)) { - Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); - } - continue; - } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify(); - unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); - unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; - unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; - j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; - if (j === undefined) { - if(unitStatic !== undefined && unit !== unitStatic) { - throw{ type: "Argument", message: "incompatible types" }; - } - values[unit] = order.length; - order.push(current); - continue; - } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify(); - if ( isMin && currentUnified.value < referenceUnified.value || - !isMin && currentUnified.value > referenceUnified.value) { - order[j] = current; - } - } - if (order.length == 1) { - return order[0]; - } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); - return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); - }, - min: function () { - return this._minmax(true, arguments); - }, - max: function () { - return this._minmax(false, arguments); - }, - "get-unit": function (n) { - return new(tree.Anonymous)(n.unit); - }, - argb: function (color) { - return new(tree.Anonymous)(color.toARGB()); - }, - percentage: function (n) { - return new(tree.Dimension)(n.value * 100, '%'); - }, - color: function (n) { - if (n instanceof tree.Quoted) { - var colorCandidate = n.value, - returnColor; - returnColor = tree.Color.fromKeyword(colorCandidate); - if (returnColor) { - return returnColor; - } - if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) { - return new(tree.Color)(colorCandidate.slice(1)); - } - throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" }; - } else { - throw { type: "Argument", message: "argument must be a string" }; - } - }, - iscolor: function (n) { - return this._isa(n, tree.Color); - }, - isnumber: function (n) { - return this._isa(n, tree.Dimension); - }, - isstring: function (n) { - return this._isa(n, tree.Quoted); - }, - iskeyword: function (n) { - return this._isa(n, tree.Keyword); - }, - isurl: function (n) { - return this._isa(n, tree.URL); - }, - ispixel: function (n) { - return this.isunit(n, 'px'); - }, - ispercentage: function (n) { - return this.isunit(n, '%'); - }, - isem: function (n) { - return this.isunit(n, 'em'); - }, - isunit: function (n, unit) { - return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False; - }, - _isa: function (n, Type) { - return (n instanceof Type) ? tree.True : tree.False; - }, - tint: function(color, amount) { - return this.mix(this.rgb(255,255,255), color, amount); - }, - shade: function(color, amount) { - return this.mix(this.rgb(0, 0, 0), color, amount); - }, - extract: function(values, index) { - index = index.value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - return Array.isArray(values.value) - ? values.value[index] : Array(values)[index]; - }, - length: function(values) { - var n = Array.isArray(values.value) ? values.value.length : 1; - return new tree.Dimension(n); - }, - - "data-uri": function(mimetypeNode, filePathNode) { - - if (typeof window !== 'undefined') { - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - - var mimetype = mimetypeNode.value; - var filePath = (filePathNode && filePathNode.value); - - var fs = require('./fs'), - path = require('path'), - useBase64 = false; - - if (arguments.length < 2) { - filePath = mimetype; - } - - var fragmentStart = filePath.indexOf('#'); - var fragment = ''; - if (fragmentStart!==-1) { - fragment = filePath.slice(fragmentStart); - filePath = filePath.slice(0, fragmentStart); - } - - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = path.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = path.join(this.currentFileInfo.entryPath, filePath); - } - } - - // detect the mimetype if not given - if (arguments.length < 2) { - var mime; - try { - mime = require('mime'); - } catch (ex) { - mime = tree._mime; - } - - mimetype = mime.lookup(filePath); - - // 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'; } - } - else { - useBase64 = /;base64$/.test(mimetype); - } - - var buf = fs.readFileSync(filePath); - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - var DATA_URI_MAX_KB = 32, - fileSizeInKB = parseInt((buf.length / 1024), 10); - if (fileSizeInKB >= DATA_URI_MAX_KB) { - - if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } - - return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); - } - } - - buf = useBase64 ? buf.toString('base64') - : encodeURIComponent(buf); - - var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; - return new(tree.URL)(new(tree.Anonymous)(uri)); - }, - - "svg-gradient": function(direction) { - - function throwArgumentDescriptor() { - throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; - } - - if (arguments.length < 3) { - throwArgumentDescriptor(); - } - var stops = Array.prototype.slice.call(arguments, 1), - gradientDirectionSvg, - gradientType = "linear", - rectangleDimension = 'x="0" y="0" width="1" height="1"', - useBase64 = true, - renderEnv = {compress: false}, - returner, - directionValue = direction.toCSS(renderEnv), - i, color, position, positionValue, alpha; - - 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 { type: "Argument", message: "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 < stops.length; i+= 1) { - if (stops[i].value) { - color = stops[i].value[0]; - position = stops[i].value[1]; - } else { - color = stops[i]; - position = undefined; - } - - if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) { - throwArgumentDescriptor(); - } - positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; - alpha = color.alpha; - returner += ''; - } - returner += '' + - ''; - - if (useBase64) { - try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation - } catch(e) { - useBase64 = false; - } - } - - returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(tree.URL)(new(tree.Anonymous)(returner)); - } -}; - -// these static methods are used as a fallback when the optional 'mime' dependency is missing -tree._mime = { - // this map is intentionally incomplete - // if you want more, install 'mime' dep - _types: { - '.htm' : 'text/html', - '.html': 'text/html', - '.gif' : 'image/gif', - '.jpg' : 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png' : 'image/png' - }, - lookup: function (filepath) { - var ext = require('path').extname(filepath), - type = tree._mime._types[ext]; - if (type === undefined) { - throw new Error('Optional dependency "mime" is required for ' + ext); - } - return type; - }, - charsets: { - lookup: function (type) { - // assumes all text types are UTF-8 - return type && (/^text\//).test(type) ? 'UTF-8' : ''; - } - } -}; - -// Math - -var mathFunctions = { - // name, unit - ceil: null, - floor: null, - sqrt: null, - abs: null, - tan: "", - sin: "", - cos: "", - atan: "rad", - asin: "rad", - acos: "rad" -}; - -function _math(fn, unit, n) { - if (!(n instanceof tree.Dimension)) { - throw { type: "Argument", message: "argument must be a number" }; - } - if (unit == null) { - unit = n.unit; - } else { - n = n.unify(); - } - return new(tree.Dimension)(fn(parseFloat(n.value)), unit); -} - -// ~ End of Math - -// Color Blending -// ref: http://www.w3.org/TR/compositing-1 - -function colorBlend(mode, color1, color2) { - var ab = color1.alpha, cb, // backdrop - as = color2.alpha, cs, // source - ar, cr, r = []; // result - - ar = as + ab * (1 - as); - for (var i = 0; i < 3; i++) { - cb = color1.rgb[i] / 255; - cs = color2.rgb[i] / 255; - cr = mode(cb, cs); - if (ar) { - cr = (as * cs + ab * (cb - - as * (cb + cs - cr))) / ar; - } - r[i] = cr * 255; - } - - return new(tree.Color)(r, ar); -} - -var colorBlendMode = { - multiply: function(cb, cs) { - return cb * cs; - }, - screen: function(cb, cs) { - return cb + cs - cb * cs; - }, - overlay: function(cb, cs) { - cb *= 2; - return (cb <= 1) - ? colorBlendMode.multiply(cb, cs) - : colorBlendMode.screen(cb - 1, cs); - }, - softlight: function(cb, cs) { - var d = 1, e = cb; - if (cs > 0.5) { - e = 1; - d = (cb > 0.25) ? Math.sqrt(cb) - : ((16 * cb - 12) * cb + 4) * cb; - } - return cb - (1 - 2 * cs) * e * (d - cb); - }, - hardlight: function(cb, cs) { - return colorBlendMode.overlay(cs, cb); - }, - difference: function(cb, cs) { - return Math.abs(cb - cs); - }, - exclusion: function(cb, cs) { - return cb + cs - 2 * cb * cs; - }, - - // non-w3c functions: - average: function(cb, cs) { - return (cb + cs) / 2; - }, - negation: function(cb, cs) { - return 1 - Math.abs(cb + cs - 1); - } -}; - -// ~ End of Color Blending - -tree.defaultFunc = { - eval: function () { - var v = this.value_, e = this.error_; - if (e) { - throw e; - } - if (v != null) { - return v ? tree.True : tree.False; - } - }, - value: function (v) { - this.value_ = v; - }, - error: function (e) { - this.error_ = e; - }, - reset: function () { - this.value_ = this.error_ = null; - } -}; - -function initFunctions() { - var f, tf = tree.functions; - - // math - for (f in mathFunctions) { - if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); - } - } - - // color blending - for (f in colorBlendMode) { - if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); - } - } - - // default - f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); - -} initFunctions(); - -function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); -} - -function scaled(n, size) { - if (n instanceof tree.Dimension && n.unit.is('%')) { - return parseFloat(n.value * size / 100); - } else { - return number(n); - } -} - -function number(n) { - if (n instanceof tree.Dimension) { - return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); - } else if (typeof(n) === 'number') { - return n; - } else { - throw { - error: "RuntimeError", - message: "color functions take numbers as parameters" - }; - } -} - -function clamp(val) { - return Math.min(1, Math.max(0, val)); -} - -tree.fround = function(env, value) { - var p = env && env.numPrecision; - //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... - return (p == null) ? value : Number((value + 2e-16).toFixed(p)); -}; - -tree.functionCall = function(env, currentFileInfo) { - this.env = env; - this.currentFileInfo = currentFileInfo; -}; - -tree.functionCall.prototype = tree.functions; - -})(require('./tree')); - -(function (tree) { - tree.colors = { - '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' - }; -})(require('./tree')); - -(function (tree) { - -tree.debugInfo = function(env, ctx, lineSeperator) { - var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { - case 'comments': - result = tree.debugInfo.asComment(ctx); - break; - case 'mediaquery': - result = tree.debugInfo.asMediaQuery(ctx); - break; - case 'all': - result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx); - break; - } - } - return result; -}; - -tree.debugInfo.asComment = function(ctx) { - return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; -}; - -tree.debugInfo.asMediaQuery = function(ctx) { - return '@media -sass-debug-info{filename{font-family:' + - ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { - if (a == '\\') { - a = '\/'; - } - return '\\' + a; - }) + - '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; -}; - -tree.find = function (obj, fun) { - for (var i = 0, r; i < obj.length; i++) { - r = fun.call(obj, obj[i]); - if (r) { return r; } - } - return null; -}; - -tree.jsify = function (obj) { - if (Array.isArray(obj.value) && (obj.value.length > 1)) { - return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; - } else { - return obj.toCSS(); - } -}; - -tree.toCSS = function (env) { - var strs = []; - this.genCSS(env, { - add: function(chunk, fileInfo, index) { - strs.push(chunk); - }, - isEmpty: function () { - return strs.length === 0; - } - }); - return strs.join(''); -}; - -tree.outputRuleset = function (env, output, rules) { - var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; - - // Compressed - if (env.compress) { - output.add('{'); - for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); - } - output.add('}'); - env.tabLevel--; - return; - } - - // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; - if (!ruleCnt) { - output.add(" {" + tabSetStr + '}'); - } else { - output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); - for (i = 1; i < ruleCnt; i++) { - output.add(tabRuleStr); - rules[i].genCSS(env, output); - } - output.add(tabSetStr + '}'); - } - - env.tabLevel--; -}; - -})(require('./tree')); - -(function (tree) { - -tree.Alpha = function (val) { - this.value = val; -}; -tree.Alpha.prototype = { - type: "Alpha", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } - return this; - }, - genCSS: function (env, output) { - output.add("alpha(opacity="); - - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - - output.add(")"); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { - this.value = value; - this.index = index; - this.mapLines = mapLines; - this.currentFileInfo = currentFileInfo; - this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike; -}; -tree.Anonymous.prototype = { - type: "Anonymous", - eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - }, - isRulesetLike: function() { - return this.rulesetLike; - }, - genCSS: function (env, output) { - output.add(this.value, this.currentFileInfo, this.index, this.mapLines); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Assignment = function (key, val) { - this.key = key; - this.value = val; -}; -tree.Assignment.prototype = { - type: "Assignment", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - eval: function (env) { - if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); - } - return this; - }, - genCSS: function (env, output) { - output.add(this.key + '='); - if (this.value.genCSS) { - this.value.genCSS(env, output); - } else { - output.add(this.value); - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -// -// A function call node. -// -tree.Call = function (name, args, index, currentFileInfo) { - this.name = name; - this.args = args; - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Call.prototype = { - type: "Call", - accept: function (visitor) { - if (this.args) { - 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, - // if this returns null or we cannot find the function, 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. - // - eval: function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - nameLC = this.name.toLowerCase(), - result, func; - - if (nameLC in tree.functions) { // 1. - try { - func = new tree.functionCall(env, this.currentFileInfo); - result = func[nameLC].apply(func, args); - if (result != null) { - return result; - } - } catch (e) { - throw { type: e.type || "Runtime", - message: "error evaluating function `" + this.name + "`" + - (e.message ? ': ' + e.message : ''), - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - return new tree.Call(this.name, args, this.index, this.currentFileInfo); - }, - - genCSS: function (env, output) { - output.add(this.name + "(", this.currentFileInfo, this.index); - - for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); - if (i + 1 < this.args.length) { - output.add(", "); - } - } - - output.add(")"); - }, - - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { -// -// RGB Colors - #ff0014, #eee -// -tree.Color = function (rgb, a) { - // - // The end goal here, is to parse the arguments - // into an integer triplet, such as `128, 255, 0` - // - // This facilitates operations and conversions. - // - if (Array.isArray(rgb)) { - this.rgb = rgb; - } else if (rgb.length == 6) { - this.rgb = rgb.match(/.{2}/g).map(function (c) { - return parseInt(c, 16); - }); - } else { - this.rgb = rgb.split('').map(function (c) { - return parseInt(c + c, 16); - }); - } - this.alpha = typeof(a) === 'number' ? a : 1; -}; - -var transparentKeyword = "transparent"; - -tree.Color.prototype = { - type: "Color", - eval: function () { return this; }, - luma: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255; - - r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); - g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); - b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); - - return 0.2126 * r + 0.7152 * g + 0.0722 * b; - }, - - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, - alpha = tree.fround(env, 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 && this.isTransparentKeyword) { - return transparentKeyword; - } - return "rgba(" + this.rgb.map(function (c) { - return clamp(Math.round(c), 255); - }).concat(clamp(alpha, 1)) - .join(',' + (compress ? '' : ' ')) + ")"; - } else { - var color = this.toRGB(); - - if (compress) { - var splitcolor = color.split(''); - - // Convert color to short format - if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { - color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. - // - operate: function (env, op, other) { - var rgb = []; - var alpha = this.alpha * (1 - other.alpha) + other.alpha; - for (var c = 0; c < 3; c++) { - rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); - } - return new(tree.Color)(rgb, alpha); - }, - - toRGB: function () { - return toHex(this.rgb); - }, - - toHSL: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2, d = max - min; - - if (max === min) { - h = s = 0; - } else { - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return { 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 - toHSV: function () { - var r = this.rgb[0] / 255, - g = this.rgb[1] / 255, - b = this.rgb[2] / 255, - a = this.alpha; - - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - if (max === 0) { - s = 0; - } else { - s = d / max; - } - - if (max === min) { - h = 0; - } else { - 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 { h: h * 360, s: s, v: v, a: a }; - }, - toARGB: function () { - return toHex([this.alpha * 255].concat(this.rgb)); - }, - compare: function (x) { - if (!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; - } -}; - -tree.Color.fromKeyword = function(keyword) { - keyword = keyword.toLowerCase(); - - if (tree.colors.hasOwnProperty(keyword)) { - // detect named color - return new(tree.Color)(tree.colors[keyword].slice(1)); - } - if (keyword === transparentKeyword) { - var transparent = new(tree.Color)([0, 0, 0], 0); - transparent.isTransparentKeyword = true; - return transparent; - } -}; - -function toHex(v) { - return '#' + v.map(function (c) { - c = clamp(Math.round(c), 255); - return (c < 16 ? '0' : '') + c.toString(16); - }).join(''); -} - -function clamp(v, max) { - return Math.min(Math.max(v, 0), max); -} - -})(require('../tree')); - -(function (tree) { - -tree.Comment = function (value, silent, index, currentFileInfo) { - this.value = value; - this.silent = !!silent; - this.currentFileInfo = currentFileInfo; -}; -tree.Comment.prototype = { - type: "Comment", - genCSS: function (env, output) { - if (this.debugInfo) { - output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index); - } - output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n - }, - toCSS: tree.toCSS, - isSilent: function(env) { - var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && !this.value.match(/^\/\*!/); - return this.silent || isReference || isCompressed; - }, - eval: function () { return this; }, - markReferenced: function () { - this.isReferenced = true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Condition = function (op, l, r, i, negate) { - this.op = op.trim(); - this.lvalue = l; - this.rvalue = r; - this.index = i; - this.negate = negate; -}; -tree.Condition.prototype = { - type: "Condition", - accept: function (visitor) { - this.lvalue = visitor.visit(this.lvalue); - this.rvalue = visitor.visit(this.rvalue); - }, - eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { - switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; - default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { - case -1: return op === '<' || op === '=<' || op === '<='; - case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; - case 1: return op === '>' || op === '>='; - } - } - })(this.op); - return this.negate ? !result : result; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.DetachedRuleset = function (ruleset, frames) { - this.ruleset = ruleset; - this.frames = frames; -}; -tree.DetachedRuleset.prototype = { - type: "DetachedRuleset", - accept: function (visitor) { - this.ruleset = visitor.visit(this.ruleset); - }, - eval: function (env) { - var frames = this.frames || env.frames.slice(0); - return new tree.DetachedRuleset(this.ruleset, frames); - }, - callEval: function (env) { - return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env); - } -}; -})(require('../tree')); - -(function (tree) { - -// -// A number with a unit -// -tree.Dimension = function (value, unit) { - this.value = parseFloat(value); - this.unit = (unit && unit instanceof tree.Unit) ? unit : - new(tree.Unit)(unit ? [unit] : undefined); -}; - -tree.Dimension.prototype = { - type: "Dimension", - accept: function (visitor) { - this.unit = visitor.visit(this.unit); - }, - eval: function (env) { - return this; - }, - toColor: function () { - return new(tree.Color)([this.value, this.value, this.value]); - }, - genCSS: function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { - throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); - } - - var value = tree.fround(env, this.value), - strValue = String(value); - - if (value !== 0 && value < 0.000001 && value > -0.000001) { - // would be output 1e-6 etc. - strValue = value.toFixed(20).replace(/0+$/, ""); - } - - if (env && env.compress) { - // Zero values doesn't need a unit - if (value === 0 && this.unit.isLength()) { - output.add(strValue); - return; - } - - // Float values doesn't need a leading zero - if (value > 0 && value < 1) { - strValue = (strValue).substr(1); - } - } - - output.add(strValue); - this.unit.genCSS(env, output); - }, - toCSS: tree.toCSS, - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2` will yield `3px`. - operate: function (env, op, other) { - /*jshint noempty:false */ - var value = tree.operate(env, op, this.value, other.value), - unit = this.unit.clone(); - - if (op === '+' || op === '-') { - if (unit.numerator.length === 0 && unit.denominator.length === 0) { - unit.numerator = other.unit.numerator.slice(0); - unit.denominator = other.unit.denominator.slice(0); - } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { - // do nothing - } else { - other = other.convertTo(this.unit.usedUnits()); - - if(env.strictUnits && other.unit.toString() !== unit.toString()) { - throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + - "' and '" + other.unit.toString() + "'."); - } - - value = tree.operate(env, op, this.value, other.value); - } - } else if (op === '*') { - unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); - unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); - unit.cancel(); - } else if (op === '/') { - unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); - unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); - unit.cancel(); - } - return new(tree.Dimension)(value, unit); - }, - - compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - }, - - unify: function () { - return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); - }, - - convertTo: function (conversions) { - var value = this.value, unit = this.unit.clone(), - i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; - - if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { - derivedConversions = {}; - derivedConversions[i] = conversions; - } - } - conversions = derivedConversions; - } - applyUnit = function (atomicUnit, denominator) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit)) { - if (denominator) { - value = value / (group[atomicUnit] / group[targetUnit]); - } else { - value = value * (group[atomicUnit] / group[targetUnit]); - } - - return targetUnit; - } - - return atomicUnit; - }; - - for (groupName in conversions) { - if (conversions.hasOwnProperty(groupName)) { - targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; - - unit.map(applyUnit); - } - } - - unit.cancel(); - - return new(tree.Dimension)(value, unit); - } -}; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } -}; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); - }, - genCSS: function (env, output) { - if (this.numerator.length >= 1) { - output.add(this.numerator[0]); - } else - if (this.denominator.length >= 1) { - output.add(this.denominator[0]); - } else - if ((!env || !env.strictUnits) && this.backupUnit) { - output.add(this.backupUnit); - } - }, - toCSS: tree.toCSS, - - toString: function () { - var i, returnStr = this.numerator.join("*"); - for (i = 0; i < this.denominator.length; i++) { - returnStr += "/" + this.denominator[i]; - } - return returnStr; - }, - - compare: function (other) { - return this.is(other.toString()) ? 0 : -1; - }, - - is: function (unitString) { - return this.toString() === unitString; - }, - - isLength: function () { - return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); - }, - - isEmpty: function () { - return this.numerator.length === 0 && this.denominator.length === 0; - }, - - isSingular: function() { - return this.numerator.length <= 1 && this.denominator.length === 0; - }, - - map: function(callback) { - var i; - - for (i = 0; i < this.numerator.length; i++) { - this.numerator[i] = callback(this.numerator[i], false); - } - - for (i = 0; i < this.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - cancel: function () { - var counter = {}, atomicUnit, i, backup; - - for (i = 0; i < this.numerator.length; i++) { - atomicUnit = this.numerator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; - } - - for (i = 0; i < this.denominator.length; i++) { - atomicUnit = this.denominator[i]; - if (!backup) { - backup = atomicUnit; - } - counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; - } - - this.numerator = []; - this.denominator = []; - - for (atomicUnit in counter) { - if (counter.hasOwnProperty(atomicUnit)) { - var count = counter[atomicUnit]; - - if (count > 0) { - for (i = 0; i < count; i++) { - this.numerator.push(atomicUnit); - } - } else if (count < 0) { - for (i = 0; i < -count; i++) { - this.denominator.push(atomicUnit); - } - } - } - } - - if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { - this.backupUnit = backup; - } - - this.numerator.sort(); - this.denominator.sort(); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { - this.name = name; - this.value = value; - if (rules) { - this.rules = rules; - this.rules.allowImports = true; - } - this.index = index; - this.currentFileInfo = currentFileInfo; - this.debugInfo = debugInfo; -}; - -tree.Directive.prototype = { - type: "Directive", - accept: function (visitor) { - var value = this.value, rules = this.rules; - if (rules) { - rules = visitor.visit(rules); - } - if (value) { - value = visitor.visit(value); - } - }, - isRulesetLike: function() { - return !this.isCharset(); - }, - isCharset: function() { - return "@charset" === this.name; - }, - genCSS: function (env, output) { - var value = this.value, rules = this.rules; - output.add(this.name, this.currentFileInfo, this.index); - if (value) { - output.add(' '); - value.genCSS(env, output); - } - if (rules) { - tree.outputRuleset(env, output, [rules]); - } else { - output.add(';'); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var value = this.value, rules = this.rules; - if (value) { - value = value.eval(env); - } - if (rules) { - rules = rules.eval(env); - rules.root = true; - } - return new(tree.Directive)(this.name, value, rules, - this.index, this.currentFileInfo, this.debugInfo); - }, - variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); }, - find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); }, - rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - markReferenced: function () { - var i, rules; - this.isReferenced = true; - if (this.rules) { - rules = this.rules.rules; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Element = function (combinator, value, index, currentFileInfo) { - this.combinator = combinator instanceof tree.Combinator ? - combinator : new(tree.Combinator)(combinator); - - if (typeof(value) === 'string') { - this.value = value.trim(); - } else if (value) { - this.value = value; - } else { - this.value = ""; - } - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Element.prototype = { - type: "Element", - accept: function (visitor) { - var value = this.value; - this.combinator = visitor.visit(this.combinator); - if (typeof value === "object") { - this.value = visitor.visit(value); - } - }, - eval: function (env) { - return new(tree.Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, - this.index, - this.currentFileInfo); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); - }, - toCSS: function (env) { - var value = (this.value.toCSS ? this.value.toCSS(env) : this.value); - if (value === '' && this.combinator.value.charAt(0) === '&') { - return ''; - } else { - return this.combinator.toCSS(env || {}) + value; - } - } -}; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; -}; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); - }, - genCSS: function (env, output) { - output.add(this.toCSS(env)); - }, - toCSS: function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; - - if (this.op) { - value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _noSpaceCombinators: { - '': true, - ' ': true, - '|': true - }, - genCSS: function (env, output) { - var spaceOrEmpty = (env.compress || this._noSpaceCombinators[this.value]) ? '' : ' '; - output.add(spaceOrEmpty + this.value + spaceOrEmpty); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { - type: "Expression", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - var returnValue, - inParenthesis = this.parens && !this.parensInOp, - doubleParen = false; - if (inParenthesis) { - env.inParenthesis(); - } - if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { - return e.eval(env); - })); - } else if (this.value.length === 1) { - if (this.value[0].parens && !this.value[0].parensInOp) { - doubleParen = true; - } - returnValue = this.value[0].eval(env); - } else { - returnValue = this; - } - if (inParenthesis) { - env.outOfParenthesis(); - } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { - returnValue = new(tree.Paren)(returnValue); - } - return returnValue; - }, - genCSS: function (env, output) { - for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i + 1 < this.value.length) { - output.add(" "); - } - } - }, - toCSS: tree.toCSS, - throwAwayComments: function () { - this.value = this.value.filter(function(v) { - return !(v instanceof tree.Comment); - }); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Extend = function Extend(selector, option, index) { - this.selector = selector; - this.option = option; - this.index = index; - this.object_id = tree.Extend.next_id++; - this.parent_ids = [this.object_id]; - - switch(option) { - case "all": - this.allowBefore = true; - this.allowAfter = true; - break; - default: - this.allowBefore = false; - this.allowAfter = false; - break; - } -}; -tree.Extend.next_id = 0; - -tree.Extend.prototype = { - type: "Extend", - accept: function (visitor) { - this.selector = visitor.visit(this.selector); - }, - eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); - }, - clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); - }, - findSelfSelectors: function (selectors) { - var selfElements = [], - i, - selectorElements; - - for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { - selectorElements[0].combinator.value = ' '; - } - selfElements = selfElements.concat(selectors[i].elements); - } - - this.selfSelectors = [{ elements: selfElements }]; - } -}; - -})(require('../tree')); - -(function (tree) { -// -// 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. -// -tree.Import = function (path, features, options, index, currentFileInfo) { - this.options = options; - this.index = index; - this.path = path; - this.features = features; - this.currentFileInfo = currentFileInfo; - - if (this.options.less !== undefined || this.options.inline) { - this.css = !this.options.less || this.options.inline; - } else { - var pathValue = this.getPath(); - if (pathValue && /css([\?;].*)?$/.test(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. -// -tree.Import.prototype = { - type: "Import", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - this.path = visitor.visit(this.path); - if (!this.options.inline && this.root) { - this.root = visitor.visit(this.root); - } - }, - genCSS: function (env, output) { - if (this.css) { - output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); - if (this.features) { - output.add(" "); - this.features.genCSS(env, output); - } - output.add(';'); - } - }, - toCSS: tree.toCSS, - getPath: function () { - if (this.path instanceof tree.Quoted) { - var path = this.path.value; - return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; - } else if (this.path instanceof tree.URL) { - return this.path.value.value; - } - return null; - }, - evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); - }, - evalPath: function (env) { - var path = this.path.eval(env); - var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - - if (!(path instanceof tree.URL)) { - if (rootpath) { - var pathValue = path.value; - // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { - path.value = rootpath +pathValue; - } - } - path.value = env.normalizePath(path.value); - } - - return path; - }, - eval: function (env) { - var ruleset, features = this.features && this.features.eval(env); - - if (this.skip) { - if (typeof this.skip === "function") { - this.skip = this.skip(); - } - if (this.skip) { - return []; - } - } - - if (this.options.inline) { - //todo needs to reference css file not import - var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); - return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; - } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); - if (!newImport.css && this.error) { - throw this.error; - } - return newImport; - } else { - ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0)); - - ruleset.evalImports(env); - - return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules; - } - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.JavaScript = function (string, index, escaped) { - this.escaped = escaped; - this.expression = string; - this.index = index; -}; -tree.JavaScript.prototype = { - type: "JavaScript", - eval: function (env) { - var result, - that = this, - context = {}; - - var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env)); - }); - - try { - expression = new(Function)('return (' + expression + ')'); - } catch (e) { - throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , - index: this.index }; - } - - var variables = env.frames[0].variables(); - for (var k in variables) { - if (variables.hasOwnProperty(k)) { - /*jshint loopfunc:true */ - context[k.slice(1)] = { - value: variables[k].value, - toJS: function () { - return this.value.eval(env).toCSS(); - } - }; - } - } - - try { - result = expression.call(context); - } catch (e) { - throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , - index: this.index }; - } - if (typeof(result) === 'number') { - return new(tree.Dimension)(result); - } else if (typeof(result) === 'string') { - return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index); - } else if (Array.isArray(result)) { - return new(tree.Anonymous)(result.join(', ')); - } else { - return new(tree.Anonymous)(result); - } - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { - type: "Keyword", - eval: function () { return this; }, - genCSS: function (env, output) { - if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } - output.add(this.value); - }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } -}; - -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); - -})(require('../tree')); - -(function (tree) { - -tree.Media = function (value, features, index, currentFileInfo) { - this.index = index; - this.currentFileInfo = currentFileInfo; - - var selectors = this.emptySelectors(); - - this.features = new(tree.Value)(features); - this.rules = [new(tree.Ruleset)(selectors, value)]; - this.rules[0].allowImports = true; -}; -tree.Media.prototype = { - type: "Media", - accept: function (visitor) { - if (this.features) { - this.features = visitor.visit(this.features); - } - if (this.rules) { - this.rules = visitor.visitArray(this.rules); - } - }, - genCSS: function (env, output) { - output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - tree.outputRuleset(env, output, this.rules); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; - } - - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); - if(this.debugInfo) { - this.rules[0].debugInfo = this.debugInfo; - media.debugInfo = this.debugInfo; - } - var strictMathBypass = false; - if (!env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - media.features = this.features.eval(env); - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - - env.mediaPath.push(media); - env.mediaBlocks.push(media); - - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); - - env.mediaPath.pop(); - - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); - }, - variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); }, - find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); }, - rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); }, - emptySelectors: function() { - var el = new(tree.Element)('', '&', this.index, this.currentFileInfo), - sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)]; - sels[0].mediaEmpty = true; - return sels; - }, - markReferenced: function () { - var i, rules = this.rules[0].rules; - this.rules[0].markReferenced(); - this.isReferenced = true; - for (i = 0; i < rules.length; i++) { - if (rules[i].markReferenced) { - rules[i].markReferenced(); - } - } - }, - - evalTop: function (env) { - var result = this; - - // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { - var selectors = this.emptySelectors(); - result = new(tree.Ruleset)(selectors, env.mediaBlocks); - result.multiMedia = true; - } - - delete env.mediaBlocks; - delete env.mediaPath; - - return result; - }, - evalNested: function (env) { - var i, value, - path = env.mediaPath.concat([this]); - - // Extract the media-query conditions separated with `,` (OR). - for (i = 0; i < path.length; i++) { - value = path[i].features instanceof tree.Value ? - path[i].features.value : path[i].features; - path[i] = Array.isArray(value) ? value : [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 - this.features = new(tree.Value)(this.permute(path).map(function (path) { - path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment); - }); - - for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(tree.Anonymous)("and")); - } - - return new(tree.Expression)(path); - })); - - // Fake a tree-node that doesn't output anything. - return new(tree.Ruleset)([], []); - }, - permute: function (arr) { - if (arr.length === 0) { - return []; - } else if (arr.length === 1) { - return arr[0]; - } else { - var result = []; - var rest = this.permute(arr.slice(1)); - for (var i = 0; i < rest.length; i++) { - for (var j = 0; j < arr[0].length; j++) { - result.push([arr[0][j]].concat(rest[i])); - } - } - return result; - } - }, - bubbleSelectors: function (selectors) { - if (!selectors) - return; - this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.Call.prototype = { - type: "MixinCall", - accept: function (visitor) { - if (this.selector) { - this.selector = visitor.visit(this.selector); - } - if (this.arguments) { - this.arguments = visitor.visitArray(this.arguments); - } - }, - eval: function (env) { - var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, - candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, - defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset; - - args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; - }); - - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector)).length > 0) { - isOneFound = true; - - // 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. - - for (m = 0; m < mixins.length; m++) { - mixin = mixins[m]; - isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { - isRecursive = true; - break; - } - } - if (isRecursive) { - continue; - } - - if (mixin.matchArgs(args, env)) { - candidate = {mixin: mixin, group: defNone}; - - if (mixin.matchCondition) { - for (f = 0; f < 2; f++) { - 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.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - count = [0, 0, 0]; - for (m = 0; m < candidates.length; m++) { - count[candidates[m].group]++; - } - - if (count[defNone] > 0) { - defaultResult = defFalse; - } else { - defaultResult = defTrue; - if ((count[defTrue] + count[defFalse]) > 1) { - throw { type: 'Runtime', - message: 'Ambiguous use of `default()` found when matching for `' - + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } - } - - for (m = 0; m < candidates.length; m++) { - candidate = candidates[m].group; - if ((candidate === defNone) || (candidate === defaultResult)) { - try { - mixin = candidates[m].mixin; - if (!(mixin instanceof tree.mixin.Definition)) { - originalRuleset = mixin.originalRuleset || mixin; - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = originalRuleset; - } - Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); - } catch (e) { - throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; - } - } - } - - if (match) { - if (!this.currentFileInfo || !this.currentFileInfo.reference) { - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - if (rule.markReferenced) { - rule.markReferenced(); - } - } - } - return rules; - } - } - } - if (isOneFound) { - throw { type: 'Runtime', - message: 'No matching definition was found for `' + this.format(args) + '`', - index: this.index, filename: this.currentFileInfo.filename }; - } else { - throw { type: 'Name', - message: this.selector.toCSS().trim() + " is undefined", - index: this.index, filename: this.currentFileInfo.filename }; - } - }, - format: function (args) { - return this.selector.toCSS().trim() + '(' + - (args ? args.map(function (a) { - var argValue = ""; - if (a.name) { - argValue += a.name + ":"; - } - if (a.value.toCSS) { - argValue += a.value.toCSS(); - } else { - argValue += "???"; - } - return argValue; - }).join(', ') : "") + ")"; - } -}; - -tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) { - this.name = name; - this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])]; - this.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.Definition.prototype = { - type: "MixinDefinition", - accept: function (visitor) { - if (this.params && this.params.length) { - this.params = visitor.visitArray(this.params); - } - this.rules = visitor.visitArray(this.rules); - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - variable: function (name) { return this.parent.variable.call(this, name); }, - variables: function () { return this.parent.variables.call(this); }, - find: function () { return this.parent.find.apply(this, arguments); }, - rulesets: function () { return this.parent.rulesets.apply(this); }, - - evalParams: function (env, mixinEnv, args, evaldArguments) { - /*jshint boss:true */ - var frame = new(tree.Ruleset)(null, null), - varargs, arg, - params = this.params.slice(0), - i, j, val, name, isNamedFound, argIndex, argsLength = 0; - - mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); - - if (args) { - args = args.slice(0); - argsLength = args.length; - - for(i = 0; i < argsLength; i++) { - arg = args[i]; - if (name = (arg && arg.name)) { - isNamedFound = false; - for(j = 0; j < params.length; j++) { - if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(tree.Rule)(name, arg.value.eval(env))); - isNamedFound = true; - break; - } - } - if (isNamedFound) { - args.splice(i, 1); - i--; - continue; - } else { - throw { type: 'Runtime', message: "Named argument for " + this.name + - ' ' + args[i].name + ' not found' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - arg = args && args[argIndex]; - - if (name = params[i].name) { - if (params[i].variadic) { - varargs = []; - for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); - } - frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env))); - } else { - val = arg && arg.value; - if (val) { - val = val.eval(env); - } else if (params[i].value) { - val = params[i].value.eval(mixinEnv); - frame.resetCache(); - } else { - throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + - ' (' + argsLength + ' for ' + this.arity + ')' }; - } - - frame.prependRule(new(tree.Rule)(name, val)); - evaldArguments[i] = val; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - argIndex++; - } - - return frame; - }, - eval: function (env) { - return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); - }, - evalCall: function (env, args, important) { - var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments), - rules, ruleset; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - ruleset = new(tree.Ruleset)(null, rules); - ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames))); - if (important) { - ruleset = this.parent.makeImportant.apply(ruleset); - } - return ruleset; - }, - matchCondition: function (args, env) { - if (this.condition && !this.condition.eval( - new(tree.evalEnv)(env, - [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables - .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames - return false; - } - return true; - }, - matchArgs: function (args, env) { - var argsLength = (args && args.length) || 0, len; - - if (! this.variadic) { - if (argsLength < this.required) { return false; } - if (argsLength > this.params.length) { return false; } - } else { - if (argsLength < (this.required - 1)) { return false; } - } - - len = Math.min(argsLength, this.arity); - - for (var i = 0; i < len; i++) { - if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { - return false; - } - } - } - return true; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Negative = function (node) { - this.value = node; -}; -tree.Negative.prototype = { - type: "Negative", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('-'); - this.value.genCSS(env, output); - }, - toCSS: tree.toCSS, - eval: function (env) { - if (env.isMathOn()) { - return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); - } - return new(tree.Negative)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Operation = function (op, operands, isSpaced) { - this.op = op.trim(); - this.operands = operands; - this.isSpaced = isSpaced; -}; -tree.Operation.prototype = { - type: "Operation", - accept: function (visitor) { - this.operands = visitor.visit(this.operands); - }, - eval: function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); - - if (env.isMathOn()) { - if (a instanceof tree.Dimension && b instanceof tree.Color) { - a = a.toColor(); - } - if (b instanceof tree.Dimension && a instanceof tree.Color) { - b = b.toColor(); - } - if (!a.operate) { - throw { type: "Operation", - message: "Operation on an invalid type" }; - } - - return a.operate(env, this.op, b); - } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); - } - }, - genCSS: function (env, output) { - this.operands[0].genCSS(env, output); - if (this.isSpaced) { - output.add(" "); - } - output.add(this.op); - if (this.isSpaced) { - output.add(" "); - } - this.operands[1].genCSS(env, output); - }, - toCSS: tree.toCSS -}; - -tree.operate = function (env, op, a, b) { - switch (op) { - case '+': return a + b; - case '-': return a - b; - case '*': return a * b; - case '/': return a / b; - } -}; - -})(require('../tree')); - - -(function (tree) { - -tree.Paren = function (node) { - this.value = node; -}; -tree.Paren.prototype = { - type: "Paren", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add('('); - this.value.genCSS(env, output); - output.add(')'); - }, - toCSS: tree.toCSS, - eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { - this.escaped = escaped; - this.value = content || ''; - this.quote = str.charAt(0); - this.index = index; - this.currentFileInfo = currentFileInfo; -}; -tree.Quoted.prototype = { - type: "Quoted", - genCSS: function (env, output) { - if (!this.escaped) { - output.add(this.quote, this.currentFileInfo, this.index); - } - output.add(this.value); - if (!this.escaped) { - output.add(this.quote); - } - }, - toCSS: tree.toCSS, - eval: function (env) { - var that = this; - var value = this.value.replace(/`([^`]+)`/g, function (_, exp) { - return new(tree.JavaScript)(exp, that.index, true).eval(env).value; - }).replace(/@\{([\w-]+)\}/g, function (_, name) { - var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); - return (v instanceof tree.Quoted) ? v.value : v.toCSS(); - }); - return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); - }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - - // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; - } else { - left = this.toCSS(); - right = x.toCSS(); - } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) { - this.name = name; - this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]); - this.important = important ? ' ' + important.trim() : ''; - this.merge = merge; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.inline = inline || false; - this.variable = (variable !== undefined) ? variable - : (name.charAt && (name.charAt(0) === '@')); -}; - -tree.Rule.prototype = { - type: "Rule", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); - try { - this.value.genCSS(env, output); - } - catch(e) { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - throw e; - } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); - }, - toCSS: tree.toCSS, - eval: function (env) { - var strictMathBypass = false, name = this.name, variable = this.variable, evaldValue; - if (typeof name !== "string") { - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - name = (name.length === 1) - && (name[0] instanceof tree.Keyword) - ? name[0].value : evalName(env, name); - variable = false; // never treat expanded interpolation as new variable name - } - if (name === "font" && !env.strictMath) { - strictMathBypass = true; - env.strictMath = true; - } - try { - evaldValue = this.value.eval(env); - - if (!this.variable && evaldValue.type === "DetachedRuleset") { - throw { message: "Rulesets cannot be evaluated on a property.", - index: this.index, filename: this.currentFileInfo.filename }; - } - - return new(tree.Rule)(name, - evaldValue, - this.important, - this.merge, - this.index, this.currentFileInfo, this.inline, - variable); - } - catch(e) { - if (typeof e.index !== 'number') { - e.index = this.index; - e.filename = this.currentFileInfo.filename; - } - throw e; - } - finally { - if (strictMathBypass) { - env.strictMath = false; - } - } - }, - makeImportant: function () { - return new(tree.Rule)(this.name, - this.value, - "!important", - this.merge, - this.index, this.currentFileInfo, this.inline); - } -}; - -function evalName(env, name) { - var value = "", i, n = name.length, - output = {add: function (s) {value += s;}}; - for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); - } - return value; -} - -})(require('../tree')); - -(function (tree) { - -tree.RulesetCall = function (variable) { - this.variable = variable; -}; -tree.RulesetCall.prototype = { - type: "RulesetCall", - accept: function (visitor) { - }, - eval: function (env) { - var detachedRuleset = new(tree.Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Ruleset = function (selectors, rules, strictImports) { - this.selectors = selectors; - this.rules = rules; - this._lookups = {}; - this.strictImports = strictImports; -}; -tree.Ruleset.prototype = { - type: "Ruleset", - accept: function (visitor) { - if (this.paths) { - visitor.visitArray(this.paths, true); - } else if (this.selectors) { - this.selectors = visitor.visitArray(this.selectors); - } - if (this.rules && this.rules.length) { - this.rules = visitor.visitArray(this.rules); - } - }, - eval: function (env) { - var thisSelectors = this.selectors, selectors, - selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false; - - if (thisSelectors && (selCnt = thisSelectors.length)) { - selectors = []; - defaultFunc.error({ - type: "Syntax", - message: "it is currently only allowed in parametric mixin guards," - }); - for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); - selectors.push(selector); - if (selector.evaldCondition) { - hasOnePassingSelector = true; - } - } - defaultFunc.reset(); - } else { - hasOnePassingSelector = true; - } - - var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), - rule, subRule; - - ruleset.originalRuleset = this; - ruleset.root = this.root; - ruleset.firstRoot = this.firstRoot; - ruleset.allowImports = this.allowImports; - - if(this.debugInfo) { - ruleset.debugInfo = this.debugInfo; - } - - if (!hasOnePassingSelector) { - rules.length = 0; - } - - // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); - - // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; - } - envSelectors.unshift(this.selectors); - - // Evaluate imports - if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); - } - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) { - rsRules[i] = rsRules[i].eval(env); - } - } - - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; - - // Evaluate mixin calls. - for (i = 0; i < rsRuleCnt; i++) { - if (rsRules[i] instanceof tree.mixin.Call) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { - if ((r instanceof 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 - return !(ruleset.variable(r.name)); - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } else if (rsRules[i] instanceof tree.RulesetCall) { - /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { - if ((r instanceof tree.Rule) && r.variable) { - // do not pollute the scope at all - return false; - } - return true; - }); - rsRules.splice.apply(rsRules, [i, 1].concat(rules)); - rsRuleCnt += rules.length - 1; - i += rules.length-1; - ruleset.resetCache(); - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; - } - } - - // Evaluate everything else - for (i = 0; i < rsRules.length; i++) { - rule = rsRules[i]; - // for rulesets, check if it is a css guard and can be removed - if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) { - // check if it can be folded in (e.g. & where) - if (rule.selectors[0].isJustParentSelector()) { - rsRules.splice(i--, 1); - - for(var j = 0; j < rule.rules.length; j++) { - subRule = rule.rules[j]; - if (!(subRule instanceof tree.Rule) || !subRule.variable) { - rsRules.splice(++i, 0, subRule); - } - } - } - } - } - - // Pop the stack - envFrames.shift(); - envSelectors.shift(); - - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); - } - } - - return ruleset; - }, - evalImports: function(env) { - var rules = this.rules, i, importRules; - if (!rules) { return; } - - for (i = 0; i < rules.length; i++) { - if (rules[i] instanceof tree.Import) { - importRules = rules[i].eval(env); - if (importRules && importRules.length) { - rules.splice.apply(rules, [i, 1].concat(importRules)); - i+= importRules.length-1; - } else { - rules.splice(i, 1, importRules); - } - this.resetCache(); - } - } - }, - makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { - if (r.makeImportant) { - return r.makeImportant(); - } else { - return r; - } - }), this.strictImports); - }, - matchArgs: function (args) { - return !args || args.length === 0; - }, - // lets you call a css selector with a guard - matchCondition: function (args, env) { - var lastSelector = this.selectors[this.selectors.length-1]; - if (!lastSelector.evaldCondition) { - return false; - } - if (lastSelector.condition && - !lastSelector.condition.eval( - new(tree.evalEnv)(env, - env.frames))) { - return false; - } - return true; - }, - resetCache: function () { - this._rulesets = null; - this._variables = null; - this._lookups = {}; - }, - variables: function () { - if (!this._variables) { - this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { - if (r instanceof tree.Rule && r.variable === true) { - hash[r.name] = r; - } - return hash; - }, {}); - } - return this._variables; - }, - variable: function (name) { - return this.variables()[name]; - }, - rulesets: function () { - if (!this.rules) { return null; } - - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, - filtRules = [], rules = this.rules, cnt = rules.length, - i, rule; - - for (i = 0; i < cnt; i++) { - rule = rules[i]; - if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) { - filtRules.push(rule); - } - } - - return filtRules; - }, - prependRule: function (rule) { - var rules = this.rules; - if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } - }, - find: function (selector, self) { - self = self || this; - var rules = [], match, - key = selector.toCSS(); - - if (key in this._lookups) { return this._lookups[key]; } - - this.rulesets().forEach(function (rule) { - if (rule !== self) { - for (var j = 0; j < rule.selectors.length; j++) { - match = selector.match(rule.selectors[j]); - if (match) { - if (selector.elements.length > match) { - Array.prototype.push.apply(rules, rule.find( - new(tree.Selector)(selector.elements.slice(match)), self)); - } else { - rules.push(rule); - } - break; - } - } - } - }); - this._lookups[key] = rules; - return rules; - }, - genCSS: function (env, output) { - var i, j, - charsetRuleNodes = [], - ruleNodes = [], - rulesetNodes = [], - rulesetNodeCnt, - debugInfo, // Line number debugging - rule, - path; - - env.tabLevel = (env.tabLevel || 0); - - if (!this.root) { - env.tabLevel++; - } - - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), - sep; - - function isRulesetLikeNode(rule, root) { - // if it has nested rules, then it should be treated like a ruleset - if (rule.rules) - return true; - - // medias and comments do not have nested rules, but should be treated like rulesets anyway - if ( (rule instanceof tree.Media) || (root && rule instanceof tree.Comment)) - return true; - - // some directives and anonumoust nodes are ruleset like, others are not - if ((rule instanceof tree.Directive) || (rule instanceof tree.Anonymous)) { - return rule.isRulesetLike(); - } - - //anything else is assumed to be a rule - return false; - } - - for (i = 0; i < this.rules.length; i++) { - rule = this.rules[i]; - if (isRulesetLikeNode(rule, this.root)) { - rulesetNodes.push(rule); - } else { - //charsets should float on top of everything - if (rule.isCharset && rule.isCharset()) { - charsetRuleNodes.push(rule); - } else { - ruleNodes.push(rule); - } - } - } - ruleNodes = charsetRuleNodes.concat(ruleNodes); - - // 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); - } - - var paths = this.paths, pathCnt = paths.length, - pathSubCnt; - - sep = env.compress ? ',' : (',\n' + tabSetStr); - - for (i = 0; i < pathCnt; i++) { - path = paths[i]; - if (!(pathSubCnt = path.length)) { continue; } - if (i > 0) { output.add(sep); } - - env.firstSelector = true; - path[0].genCSS(env, output); - - env.firstSelector = false; - for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); - } - } - - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); - } - - // Compile rules and rulesets - for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; - } - - if (rule.genCSS) { - rule.genCSS(env, output); - } else if (rule.value) { - output.add(rule.value.toString()); - } - - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); - } else { - env.lastRule = false; - } - } - - if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; - } - - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); - rulesetNodeCnt = rulesetNodes.length; - if (rulesetNodeCnt) { - if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); - for (i = 1; i < rulesetNodeCnt; i++) { - if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); - } - } - - if (!output.isEmpty() && !env.compress && this.firstRoot) { - output.add('\n'); - } - }, - - toCSS: tree.toCSS, - - markReferenced: function () { - if (!this.selectors) { - return; - } - for (var s = 0; s < this.selectors.length; s++) { - this.selectors[s].markReferenced(); - } - }, - - joinSelectors: function (paths, context, selectors) { - for (var s = 0; s < selectors.length; s++) { - this.joinSelector(paths, context, selectors[s]); - } - }, - - joinSelector: function (paths, context, selector) { - - var i, j, k, - hasParentSelector, newSelectors, el, sel, parentSel, - newSelectorPath, afterParentJoin, newJoinedSelector, - newJoinedSelectorEmpty, lastSelector, currentElements, - selectorsMultiplied; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - if (el.value === '&') { - hasParentSelector = true; - } - } - - if (!hasParentSelector) { - if (context.length > 0) { - for (i = 0; i < context.length; i++) { - paths.push(context[i].concat(selector)); - } - } - else { - paths.push([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 = []; - // 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 = [[]]; - - for (i = 0; i < selector.elements.length; i++) { - el = selector.elements[i]; - // non parent reference elements just get added - if (el.value !== "&") { - currentElements.push(el); - } else { - // the new list of selectors to add - selectorsMultiplied = []; - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - // loop through our current selectors - for (j = 0; j < newSelectors.length; j++) { - sel = newSelectors[j]; - // 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.length === 0) { - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if (sel.length > 0) { - sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo)); - } - selectorsMultiplied.push(sel); - } - else { - // and the parent selectors - for (k = 0; k < context.length; k++) { - parentSel = context[k]; - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - newSelectorPath = []; - // selectors from the parent after the join - afterParentJoin = []; - 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.length > 0) { - newSelectorPath = sel.slice(0); - lastSelector = newSelectorPath.pop(); - newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); - newJoinedSelectorEmpty = false; - } - else { - newJoinedSelector = selector.createDerived([]); - } - - //put together the parent selectors after the join - if (parentSel.length > 1) { - afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); - } - - if (parentSel.length > 0) { - newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); - newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); - } - - if (!newJoinedSelectorEmpty) { - // now add the joined selector - newSelectorPath.push(newJoinedSelector); - } - - // and the rest of the parent - newSelectorPath = newSelectorPath.concat(afterParentJoin); - - // add that to our new set of selectors - selectorsMultiplied.push(newSelectorPath); - } - } - } - - // our new selectors has been multiplied, so reset the state - newSelectors = selectorsMultiplied; - currentElements = []; - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if (currentElements.length > 0) { - this.mergeElementsOnToSelectors(currentElements, newSelectors); - } - - for (i = 0; i < newSelectors.length; i++) { - if (newSelectors[i].length > 0) { - paths.push(newSelectors[i]); - } - } - }, - - mergeElementsOnToSelectors: function(elements, selectors) { - var i, sel; - - if (selectors.length === 0) { - selectors.push([ new(tree.Selector)(elements) ]); - return; - } - - for (i = 0; i < selectors.length; i++) { - sel = selectors[i]; - - // if the previous thing in sel is a parent this needs to join on to it - if (sel.length > 0) { - sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); - } - else { - sel.push(new(tree.Selector)(elements)); - } - } - } -}; -})(require('../tree')); - -(function (tree) { - -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { - this.elements = elements; - this.extendList = extendList; - this.condition = condition; - this.currentFileInfo = currentFileInfo || {}; - this.isReferenced = isReferenced; - if (!condition) { - this.evaldCondition = true; - } -}; -tree.Selector.prototype = { - type: "Selector", - accept: function (visitor) { - if (this.elements) { - this.elements = visitor.visitArray(this.elements); - } - if (this.extendList) { - this.extendList = visitor.visitArray(this.extendList); - } - if (this.condition) { - this.condition = visitor.visit(this.condition); - } - }, - createDerived: function(elements, extendList, evaldCondition) { - evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); - newSelector.evaldCondition = evaldCondition; - newSelector.mediaEmpty = this.mediaEmpty; - return newSelector; - }, - match: function (other) { - var elements = this.elements, - len = elements.length, - olen, i; - - other.CacheElements(); - - olen = other._elements.length; - if (olen === 0 || len < olen) { - return 0; - } else { - for (i = 0; i < olen; i++) { - if (elements[i].value !== other._elements[i]) { - return 0; - } - } - } - - return olen; // return number of matched elements - }, - CacheElements: function(){ - var css = '', len, v, i; - - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; - } - - this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - - } - }, - isJustParentSelector: function() { - return !this.mediaEmpty && - this.elements.length === 1 && - this.elements[0].value === '&' && - (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); - }, - eval: function (env) { - var evaldCondition = this.condition && this.condition.eval(env), - elements = this.elements, extendList = this.extendList; - - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); - - return this.createDerived(elements, extendList, evaldCondition); - }, - genCSS: function (env, output) { - var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { - output.add(' ', this.currentFileInfo, this.index); - } - if (!this._css) { - //TODO caching? speed comparison? - for(i = 0; i < this.elements.length; i++) { - element = this.elements[i]; - element.genCSS(env, output); - } - } - }, - toCSS: tree.toCSS, - markReferenced: function () { - this.isReferenced = true; - }, - getIsReferenced: function() { - return !this.currentFileInfo.reference || this.isReferenced; - }, - getIsOutput: function() { - return this.evaldCondition; - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.UnicodeDescriptor = function (value) { - this.value = value; -}; -tree.UnicodeDescriptor.prototype = { - type: "UnicodeDescriptor", - genCSS: function (env, output) { - output.add(this.value); - }, - toCSS: tree.toCSS, - eval: function () { return this; } -}; - -})(require('../tree')); - -(function (tree) { - -tree.URL = function (val, currentFileInfo, isEvald) { - this.value = val; - this.currentFileInfo = currentFileInfo; - this.isEvald = isEvald; -}; -tree.URL.prototype = { - type: "Url", - accept: function (visitor) { - this.value = visitor.visit(this.value); - }, - genCSS: function (env, output) { - output.add("url("); - this.value.genCSS(env, output); - output.add(")"); - }, - toCSS: tree.toCSS, - eval: function (ctx) { - var val = this.value.eval(ctx), - rootpath; - - if (!this.isEvald) { - // Add the base path if the URL is relative - rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { - if (!val.quote) { - rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); - } - val.value = rootpath + val.value; - } - - val.value = ctx.normalizePath(val.value); - - // Add url args if enabled - if (ctx.urlArgs) { - if (!val.value.match(/^\s*data:/)) { - var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; - if (val.value.indexOf('#') !== -1) { - val.value = val.value.replace('#', urlArgs + '#'); - } else { - val.value += urlArgs; - } - } - } - } - - return new(tree.URL)(val, this.currentFileInfo, true); - } -}; - -})(require('../tree')); - -(function (tree) { - -tree.Value = function (value) { - this.value = value; -}; -tree.Value.prototype = { - type: "Value", - accept: function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } - }, - eval: function (env) { - if (this.value.length === 1) { - return this.value[0].eval(env); - } else { - return new(tree.Value)(this.value.map(function (v) { - return v.eval(env); - })); - } - }, - genCSS: function (env, output) { - var i; - for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); - if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); - } - } - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); - -(function (tree) { - -tree.Variable = function (name, index, currentFileInfo) { - this.name = name; - this.index = index; - this.currentFileInfo = currentFileInfo || {}; -}; -tree.Variable.prototype = { - type: "Variable", - eval: function (env) { - var variable, name = this.name; - - if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; - } - - if (this.evaluating) { - throw { type: 'Name', - message: "Recursive variable definition for " + name, - filename: this.currentFileInfo.file, - index: this.index }; - } - - this.evaluating = true; - - variable = tree.find(env.frames, function (frame) { - var v = frame.variable(name); - if (v) { - return v.value.eval(env); - } - }); - if (variable) { - this.evaluating = false; - return variable; - } else { - throw { type: 'Name', - message: "variable " + name + " is undefined", - filename: this.currentFileInfo.filename, - index: this.index }; - } - } -}; - -})(require('../tree')); - -(function (tree) { - - var parseCopyProperties = [ - 'paths', // option - unmodified - paths to search for imports on - 'optimization', // option - optimization level (for the chunker) - 'files', // list of files that have been imported, used for import-once - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore - 'relativeUrls', // option - whether to adjust URL's to be relative - 'rootpath', // option - rootpath to append to URL's - 'strictImports', // option - - 'insecure', // option - whether to allow imports from insecure ssl hosts - 'dumpLineNumbers', // option - whether to dump line numbers - 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported - 'syncImport', // option - whether to import synchronously - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'mime', // browser only - mime type for sheet import - 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. - ]; - - //currentFileInfo = { - // 'relativeUrls' - option - whether to adjust URL's to be relative - // 'filename' - full resolved filename of current file - // 'rootpath' - path to append to normal URLs for this node - // 'currentDirectory' - path to the current file, absolute - // 'rootFilename' - filename of the base file - // 'entryPath' - absolute path to the entry file - // 'reference' - whether the file should not be output and only output parts that are referenced - - tree.parseEnv = function(options) { - copyFromOriginal(options, this, parseCopyProperties); - - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } - - if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } - }; - - var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity - 'compress', // whether to compress - 'yuicompress', // whether to compress with the outside tool yui compressor - 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) - 'strictMath', // whether math has to be within parenthesis - 'strictUnits', // whether units need to evaluate correctly - 'cleancss', // whether to compress with clean-css - 'sourceMap', // whether to output a source map - 'importMultiple', // whether we are currently importing multiple copies - 'urlArgs' // whether to add args into url tokens - ]; - - tree.evalEnv = function(options, frames) { - copyFromOriginal(options, this, evalCopyProperties); - - this.frames = frames || []; - }; - - tree.evalEnv.prototype.inParenthesis = function () { - if (!this.parensStack) { - this.parensStack = []; - } - this.parensStack.push(true); - }; - - tree.evalEnv.prototype.outOfParenthesis = function () { - this.parensStack.pop(); - }; - - tree.evalEnv.prototype.isMathOn = function () { - return this.strictMath ? (this.parensStack && this.parensStack.length) : true; - }; - - tree.evalEnv.prototype.isPathRelative = function (path) { - return !/^(?:[a-z-]+:|\/)/.test(path); - }; - - tree.evalEnv.prototype.normalizePath = function( path ) { - var - segments = path.split("/").reverse(), - segment; - - path = []; - while (segments.length !== 0 ) { - segment = segments.pop(); - switch( segment ) { - case ".": - break; - case "..": - if ((path.length === 0) || (path[path.length - 1] === "..")) { - path.push( segment ); - } else { - path.pop(); - } - break; - default: - path.push( segment ); - break; - } - } - - return path.join("/"); - }; - - //todo - do the same for the toCSS env - //tree.toCSSEnv = function (options) { - //}; - - var copyFromOriginal = function(original, destination, propertiesToCopy) { - if (!original) { return; } - - for(var i = 0; i < propertiesToCopy.length; i++) { - if (original.hasOwnProperty(propertiesToCopy[i])) { - destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; - } - } - }; - -})(require('./tree')); - -(function (tree) { - - var _visitArgs = { visitDeeper: true }, - _hasIndexed = false; - - function _noop(node) { - return node; - } - - function indexNodeTypes(parent, ticker) { - // add .typeIndex to tree node types for lookup table - var key, child; - for (key in parent) { - if (parent.hasOwnProperty(key)) { - child = parent[key]; - switch (typeof child) { - case "function": - // ignore bound functions directly on tree which do not have a prototype - // or aren't nodes - if (child.prototype && child.prototype.type) { - child.prototype.typeIndex = ticker++; - } - break; - case "object": - ticker = indexNodeTypes(child, ticker); - break; - } - } - } - return ticker; - } - - tree.visitor = function(implementation) { - this._implementation = implementation; - this._visitFnCache = []; - - if (!_hasIndexed) { - indexNodeTypes(tree, 1); - _hasIndexed = true; - } - }; - - tree.visitor.prototype = { - visit: function(node) { - if (!node) { - return node; - } - - var nodeTypeIndex = node.typeIndex; - if (!nodeTypeIndex) { - return node; - } - - var visitFnCache = this._visitFnCache, - impl = this._implementation, - aryIndx = nodeTypeIndex << 1, - outAryIndex = aryIndx | 1, - func = visitFnCache[aryIndx], - funcOut = visitFnCache[outAryIndex], - visitArgs = _visitArgs, - fnName; - - visitArgs.visitDeeper = true; - - if (!func) { - fnName = "visit" + node.type; - func = impl[fnName] || _noop; - funcOut = impl[fnName + "Out"] || _noop; - visitFnCache[aryIndx] = func; - visitFnCache[outAryIndex] = funcOut; - } - - if (func !== _noop) { - var newNode = func.call(impl, node, visitArgs); - if (impl.isReplacing) { - node = newNode; - } - } - - if (visitArgs.visitDeeper && node && node.accept) { - node.accept(this); - } - - if (funcOut != _noop) { - funcOut.call(impl, node); - } - - return node; - }, - visitArray: function(nodes, nonReplacing) { - if (!nodes) { - return nodes; - } - - var cnt = nodes.length, i; - - // Non-replacing - if (nonReplacing || !this._implementation.isReplacing) { - for (i = 0; i < cnt; i++) { - this.visit(nodes[i]); - } - return nodes; - } - - // Replacing - var out = []; - for (i = 0; i < cnt; i++) { - var evald = this.visit(nodes[i]); - if (!evald.splice) { - out.push(evald); - } else if (evald.length) { - this.flatten(evald, out); - } - } - return out; - }, - flatten: function(arr, out) { - if (!out) { - out = []; - } - - var cnt, i, item, - nestedCnt, j, nestedItem; - - for (i = 0, cnt = arr.length; i < cnt; i++) { - item = arr[i]; - if (!item.splice) { - out.push(item); - continue; - } - - for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { - nestedItem = item[j]; - if (!nestedItem.splice) { - out.push(nestedItem); - } else if (nestedItem.length) { - this.flatten(nestedItem, out); - } - } - } - - return out; - } - }; - -})(require('./tree')); -(function (tree) { - tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { - this._visitor = new tree.visitor(this); - this._importer = importer; - this._finish = finish; - this.env = evalEnv || new tree.evalEnv(); - this.importCount = 0; - this.onceFileDetectionMap = onceFileDetectionMap || {}; - this.recursionDetector = {}; - if (recursionDetector) { - for(var fullFilename in recursionDetector) { - if (recursionDetector.hasOwnProperty(fullFilename)) { - this.recursionDetector[fullFilename] = true; - } - } - } - }; - - tree.importVisitor.prototype = { - isReplacing: true, - run: function (root) { - var error; - try { - // process the contents - this._visitor.visit(root); - } - catch(e) { - error = e; - } - - this.isFinished = true; - - if (this.importCount === 0) { - this._finish(error); - } - }, - visitImport: function (importNode, visitArgs) { - var importVisitor = this, - evaldImportNode, - inlineCSS = importNode.options.inline; - - if (!importNode.css || inlineCSS) { - - try { - evaldImportNode = importNode.evalForImport(this.env); - } catch(e){ - if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } - // attempt to eval properly and treat as css - importNode.css = true; - // if that fails, this error will be thrown - importNode.error = e; - } - - if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { - importNode = evaldImportNode; - this.importCount++; - var env = new tree.evalEnv(this.env, this.env.frames.slice(0)); - - if (importNode.options.multiple) { - env.importMultiple = true; - } - - this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { - if (e && !e.filename) { - e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; - } - - var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - if (!env.importMultiple) { - if (duplicateImport) { - importNode.skip = true; - } else { - importNode.skip = function() { - if (fullPath in importVisitor.onceFileDetectionMap) { - return true; - } - importVisitor.onceFileDetectionMap[fullPath] = true; - return false; - }; - } - } - - var subFinish = function(e) { - importVisitor.importCount--; - - if (importVisitor.importCount === 0 && importVisitor.isFinished) { - importVisitor._finish(e); - } - }; - - if (root) { - importNode.root = root; - importNode.importedFilename = fullPath; - - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { - importVisitor.recursionDetector[fullPath] = true; - new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) - .run(root); - return; - } - } - - subFinish(); - }); - } - } - visitArgs.visitDeeper = false; - return importNode; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - return ruleNode; - }, - visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); - return directiveNode; - }, - visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); - return mixinDefinitionNode; - }, - visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); - }, - visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); - return rulesetNode; - }, - visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); - }, - visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.rules[0]); - return mediaNode; - }, - visitMediaOut: function (mediaNode) { - this.env.frames.shift(); - } - }; - -})(require('./tree')); -(function (tree) { - tree.joinSelectorVisitor = function() { - this.contexts = [[]]; - this._visitor = new tree.visitor(this); - }; - - tree.joinSelectorVisitor.prototype = { - run: function (root) { - return this._visitor.visit(root); - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1], - paths = [], selectors; - - this.contexts.push(paths); - - if (! rulesetNode.root) { - selectors = rulesetNode.selectors; - if (selectors) { - selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); - rulesetNode.selectors = selectors.length ? selectors : (selectors = null); - if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } - } - if (!selectors) { rulesetNode.rules = null; } - rulesetNode.paths = paths; - } - }, - visitRulesetOut: function (rulesetNode) { - this.contexts.length = this.contexts.length - 1; - }, - visitMedia: function (mediaNode, visitArgs) { - var context = this.contexts[this.contexts.length - 1]; - mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); - } - }; - -})(require('./tree')); -(function (tree) { - tree.toCSSVisitor = function(env) { - this._visitor = new tree.visitor(this); - this._env = env; - }; - - tree.toCSSVisitor.prototype = { - isReplacing: true, - run: function (root) { - return this._visitor.visit(root); - }, - - visitRule: function (ruleNode, visitArgs) { - if (ruleNode.variable) { - return []; - } - return ruleNode; - }, - - visitMixinDefinition: function (mixinNode, visitArgs) { - // 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 = []; - return []; - }, - - visitExtend: function (extendNode, visitArgs) { - return []; - }, - - visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { - return []; - } - return commentNode; - }, - - visitMedia: function(mediaNode, visitArgs) { - mediaNode.accept(this._visitor); - visitArgs.visitDeeper = false; - - if (!mediaNode.rules.length) { - return []; - } - return mediaNode; - }, - - visitDirective: function(directiveNode, visitArgs) { - if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { - return []; - } - 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 (this.charset) { - if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); - comment.debugInfo = directiveNode.debugInfo; - return this._visitor.visit(comment); - } - return []; - } - this.charset = true; - } - if (directiveNode.rules && directiveNode.rules.rules) { - this._mergeRules(directiveNode.rules.rules); - } - return directiveNode; - }, - - checkPropertiesInRoot: function(rules) { - var ruleNode; - for(var i = 0; i < rules.length; i++) { - ruleNode = rules[i]; - if (ruleNode instanceof tree.Rule && !ruleNode.variable) { - throw { message: "properties must be inside selector blocks, they cannot be in the root.", - index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; - } - } - }, - - visitRuleset: function (rulesetNode, visitArgs) { - var rule, rulesets = []; - if (rulesetNode.firstRoot) { - this.checkPropertiesInRoot(rulesetNode.rules); - } - if (! rulesetNode.root) { - if (rulesetNode.paths) { - rulesetNode.paths = rulesetNode.paths - .filter(function(p) { - var i; - if (p[0].elements[0].combinator.value === ' ') { - p[0].elements[0].combinator = new(tree.Combinator)(''); - } - for(i = 0; i < p.length; i++) { - if (p[i].getIsReferenced() && p[i].getIsOutput()) { - return true; - } - } - return false; - }); - } - - // Compile rules and rulesets - var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; - for (var i = 0; i < nodeRuleCnt; ) { - rule = nodeRules[i]; - if (rule && rule.rules) { - // visit because we are moving them out from being a child - rulesets.push(this._visitor.visit(rule)); - nodeRules.splice(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._visitor); - } else { - rulesetNode.rules = null; - } - visitArgs.visitDeeper = false; - - nodeRules = rulesetNode.rules; - if (nodeRules) { - this._mergeRules(nodeRules); - nodeRules = rulesetNode.rules; - } - if (nodeRules) { - this._removeDuplicateRules(nodeRules); - nodeRules = rulesetNode.rules; - } - - // now decide whether we keep the ruleset - if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { - rulesets.splice(0, 0, rulesetNode); - } - } else { - rulesetNode.accept(this._visitor); - visitArgs.visitDeeper = false; - if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { - rulesets.splice(0, 0, rulesetNode); - } - } - if (rulesets.length === 1) { - return rulesets[0]; - } - return rulesets; - }, - - _removeDuplicateRules: function(rules) { - if (!rules) { return; } - - // remove duplicates - var ruleCache = {}, - ruleList, rule, i; - - for(i = rules.length - 1; i >= 0 ; i--) { - rule = rules[i]; - if (rule instanceof tree.Rule) { - if (!ruleCache[rule.name]) { - ruleCache[rule.name] = rule; - } else { - ruleList = ruleCache[rule.name]; - if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; - } - var ruleCSS = rule.toCSS(this._env); - if (ruleList.indexOf(ruleCSS) !== -1) { - rules.splice(i, 1); - } else { - ruleList.push(ruleCSS); - } - } - } - } - }, - - _mergeRules: function (rules) { - if (!rules) { return; } - - var groups = {}, - parts, - rule, - key; - - for (var i = 0; i < rules.length; i++) { - rule = rules[i]; - - if ((rule instanceof tree.Rule) && rule.merge) { - key = [rule.name, - rule.important ? "!" : ""].join(","); - - if (!groups[key]) { - groups[key] = []; - } else { - rules.splice(i--, 1); - } - - groups[key].push(rule); - } - } - - Object.keys(groups).map(function (k) { - - function toExpression(values) { - return new (tree.Expression)(values.map(function (p) { - return p.value; - })); - } - - function toValue(values) { - return new (tree.Value)(values.map(function (p) { - return p; - })); - } - - parts = groups[k]; - - if (parts.length > 1) { - rule = parts[0]; - var spacedGroups = []; - var lastSpacedGroup = []; - parts.map(function (p) { - if (p.merge==="+") { - if (lastSpacedGroup.length > 0) { - spacedGroups.push(toExpression(lastSpacedGroup)); - } - lastSpacedGroup = []; - } - lastSpacedGroup.push(p); - }); - spacedGroups.push(toExpression(lastSpacedGroup)); - rule.value = toValue(spacedGroups); - } - }); - } - }; - -})(require('./tree')); -(function (tree) { - /*jshint loopfunc:true */ - - tree.extendFinderVisitor = function() { - this._visitor = new tree.visitor(this); - this.contexts = []; - this.allExtendsStack = [[]]; - }; - - tree.extendFinderVisitor.prototype = { - run: function (root) { - root = this._visitor.visit(root); - root.allExtends = this.allExtendsStack[0]; - return root; - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - - var i, j, extend, allSelectorsExtendList = [], extendList; - - // get &:extend(.a); rules which apply to all selectors in this ruleset - var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; - for(i = 0; i < ruleCnt; i++) { - if (rulesetNode.rules[i] instanceof tree.Extend) { - allSelectorsExtendList.push(rules[i]); - 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 - var paths = rulesetNode.paths; - for(i = 0; i < paths.length; i++) { - var selectorPath = paths[i], - selector = selectorPath[selectorPath.length - 1], - selExtendList = selector.extendList; - - extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) - : allSelectorsExtendList; - - if (extendList) { - extendList = extendList.map(function(allSelectorsExtend) { - return allSelectorsExtend.clone(); - }); - } - - for(j = 0; j < extendList.length; j++) { - this.foundExtends = true; - extend = extendList[j]; - extend.findSelfSelectors(selectorPath); - extend.ruleset = rulesetNode; - if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } - this.allExtendsStack[this.allExtendsStack.length-1].push(extend); - } - } - - this.contexts.push(rulesetNode.selectors); - }, - visitRulesetOut: function (rulesetNode) { - if (!rulesetNode.root) { - this.contexts.length = this.contexts.length - 1; - } - }, - visitMedia: function (mediaNode, visitArgs) { - mediaNode.allExtends = []; - this.allExtendsStack.push(mediaNode.allExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - directiveNode.allExtends = []; - this.allExtendsStack.push(directiveNode.allExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - - tree.processExtendsVisitor = function() { - this._visitor = new tree.visitor(this); - }; - - tree.processExtendsVisitor.prototype = { - run: function(root) { - var extendFinder = new tree.extendFinderVisitor(); - extendFinder.run(root); - if (!extendFinder.foundExtends) { return root; } - root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); - this.allExtendsStack = [root.allExtends]; - return this._visitor.visit(root); - }, - doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { - // - // 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 - - var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; - - iterationCount = iterationCount || 0; - - //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; extendIndex < extendsList.length; extendIndex++){ - for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ - - extend = extendsList[extendIndex]; - targetExtend = extendsListTarget[targetExtendIndex]; - - // look for circular references - if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } - - // find a match in the target extends self selector (the bit before :extend) - selectorPath = [targetExtend.selfSelectors[0]]; - matches = extendVisitor.findMatch(extend, selectorPath); - - if (matches.length) { - - // we found a match, so for each self selector.. - extend.selfSelectors.forEach(function(selfSelector) { - - // process the extend as usual - newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); - - // but now we create a new extend from it - newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); - newExtend.selfSelectors = newSelector; - - // add the extend onto the list of extends for that selector - newSelector[newSelector.length-1].extendList = [newExtend]; - - // record that we need to add it. - extendsToAdd.push(newExtend); - newExtend.ruleset = targetExtend.ruleset; - - //remember its parents for circular references - newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); - } - }); - } - } - } - - if (extendsToAdd.length) { - // try to detect circular references to stop a stack overflow. - // may no longer be needed. - this.extendChainCount++; - if (iterationCount > 100) { - var selectorOne = "{unable to calculate}"; - var selectorTwo = "{unable to calculate}"; - try - { - selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); - selectorTwo = extendsToAdd[0].selector.toCSS(); - } - catch(e) {} - throw {message: "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... - return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); - } else { - return extendsToAdd; - } - }, - visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitSelector: function (selectorNode, visitArgs) { - visitArgs.visitDeeper = false; - }, - visitRuleset: function (rulesetNode, visitArgs) { - if (rulesetNode.root) { - return; - } - var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - - for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { - for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { - selectorPath = rulesetNode.paths[pathIndex]; - - // extending extends happens initially, before the main pass - if (rulesetNode.extendOnEveryPath) { continue; } - var extendList = selectorPath[selectorPath.length-1].extendList; - if (extendList && extendList.length) { continue; } - - matches = this.findMatch(allExtends[extendIndex], selectorPath); - - if (matches.length) { - - allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { - selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); - }); - } - } - } - rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); - }, - findMatch: function (extend, haystackSelectorPath) { - // - // 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 - // - var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, - targetCombinator, i, - extendVisitor = this, - needleElements = extend.selector.elements, - potentialMatches = [], potentialMatch, matches = []; - - // loop through the haystack elements - for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { - hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; - - for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); - } - - for(i = 0; i < potentialMatches.length; i++) { - potentialMatch = potentialMatches[i]; - - // 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.value; - if (targetCombinator === '' && hackstackElementIndex === 0) { - targetCombinator = ' '; - } - - // if we don't match, null our match to indicate failure - if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || - (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { - potentialMatch = null; - } else { - potentialMatch.matched++; - } - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if (potentialMatch) { - potentialMatch.finished = potentialMatch.matched === needleElements.length; - if (potentialMatch.finished && - (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { - 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 = needleElements.length; - potentialMatch.endPathIndex = haystackSelectorIndex; - potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match - potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again - matches.push(potentialMatch); - } - } else { - potentialMatches.splice(i, 1); - i--; - } - } - } - } - return matches; - }, - isElementValuesEqual: function(elementValue1, elementValue2) { - if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { - return elementValue1 === elementValue2; - } - if (elementValue1 instanceof tree.Attribute) { - 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; - elementValue2 = elementValue2.value.value || elementValue2.value; - return elementValue1 === elementValue2; - } - elementValue1 = elementValue1.value; - elementValue2 = elementValue2.value; - if (elementValue1 instanceof tree.Selector) { - if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { - return false; - } - for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - - newElements = selector.elements - .slice(currentSelectorPathElementIndex, match.index) - .concat([firstElement]) - .concat(replacementSelector.elements.slice(1)); - - if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { - path[path.length - 1].elements = - path[path.length - 1].elements.concat(newElements); - } else { - path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); - - path.push(new tree.Selector( - newElements - )); - } - currentSelectorPathIndex = match.endPathIndex; - currentSelectorPathElementIndex = match.endPathElementIndex; - if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { - currentSelectorPathElementIndex = 0; - currentSelectorPathIndex++; - } - } - - if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { - path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); - currentSelectorPathIndex++; - } - - path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); - - return path; - }, - visitRulesetOut: function (rulesetNode) { - }, - visitMedia: function (mediaNode, visitArgs) { - var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitMediaOut: function (mediaNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - }, - visitDirective: function (directiveNode, visitArgs) { - var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); - newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); - this.allExtendsStack.push(newAllExtends); - }, - visitDirectiveOut: function (directiveNode) { - this.allExtendsStack.length = this.allExtendsStack.length - 1; - } - }; - -})(require('./tree')); - -(function (tree) { - - tree.sourceMapOutput = function (options) { - this._css = []; - this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; - this._contentsMap = options.contentsMap; - this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; - this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; - if (options.sourceMapBasepath) { - this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); - } - this._sourceMapRootpath = options.sourceMapRootpath; - this._outputSourceFiles = options.outputSourceFiles; - this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator; - - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - - this._lineNumber = 0; - this._column = 0; - }; - - tree.sourceMapOutput.prototype.normalizeFilename = function(filename) { - filename = filename.replace(/\\/g, '/'); - - if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { - filename = filename.substring(this._sourceMapBasepath.length); - if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { - filename = filename.substring(1); - } - } - return (this._sourceMapRootpath || "") + filename; - }; - - tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { - - //ignore adding empty strings - if (!chunk) { - return; - } - - var lines, - sourceLines, - columns, - sourceColumns, - i; - - if (fileInfo) { - var inputSource = this._contentsMap[fileInfo.filename]; - - // remove vars/banner added to the top of the file - if (this._contentsIgnoredCharsMap[fileInfo.filename]) { - // adjust the index - index -= this._contentsIgnoredCharsMap[fileInfo.filename]; - if (index < 0) { index = 0; } - // adjust the source - inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); - } - inputSource = inputSource.substring(0, index); - sourceLines = inputSource.split("\n"); - sourceColumns = sourceLines[sourceLines.length-1]; - } - - lines = chunk.split("\n"); - columns = lines[lines.length-1]; - - if (fileInfo) { - if (!mapLines) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, - original: { line: sourceLines.length, column: sourceColumns.length}, - source: this.normalizeFilename(fileInfo.filename)}); - } else { - for(i = 0; i < lines.length; i++) { - this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, - original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, - source: this.normalizeFilename(fileInfo.filename)}); - } - } - } - - if (lines.length === 1) { - this._column += columns.length; - } else { - this._lineNumber += lines.length - 1; - this._column = columns.length; - } - - this._css.push(chunk); - }; - - tree.sourceMapOutput.prototype.isEmpty = function() { - return this._css.length === 0; - }; - - tree.sourceMapOutput.prototype.toCSS = function(env) { - this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); - - if (this._outputSourceFiles) { - for(var filename in this._contentsMap) { - if (this._contentsMap.hasOwnProperty(filename)) - { - var source = this._contentsMap[filename]; - if (this._contentsIgnoredCharsMap[filename]) { - source = source.slice(this._contentsIgnoredCharsMap[filename]); - } - this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); - } - } - } - - this._rootNode.genCSS(env, this); - - if (this._css.length > 0) { - var sourceMapURL, - sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; - } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); - } - - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); - } else { - sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent); - } - - if (sourceMapURL) { - this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); - } - } - - return this._css.join(''); - }; - -})(require('./tree')); - -// wraps the source-map code in a less module -(function() { - less.modules["source-map"] = function() { - -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ - -/** - * Define a module along with a payload. - * @param {string} moduleName Name for the payload - * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec - * @param {function} payload Function with (require, exports, module) params - */ -function define(moduleName, deps, payload) { - if (typeof moduleName != "string") { - throw new TypeError('Expected string, got: ' + moduleName); - } - - if (arguments.length == 2) { - payload = deps; - } - - if (moduleName in define.modules) { - throw new Error("Module already defined: " + moduleName); - } - define.modules[moduleName] = payload; -}; - -/** - * The global store of un-instantiated modules - */ -define.modules = {}; - - -/** - * We invoke require() in the context of a Domain so we can have multiple - * sets of modules running separate from each other. - * This contrasts with JSMs which are singletons, Domains allows us to - * optionally load a CommonJS module twice with separate data each time. - * Perhaps you want 2 command lines with a different set of commands in each, - * for example. - */ -function Domain() { - this.modules = {}; - this._currentModule = null; -} - -(function () { - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * There are 2 ways to call this, either with an array of dependencies and a - * callback to call when the dependencies are found (which can happen - * asynchronously in an in-page context) or with a single string an no callback - * where the dependency is resolved synchronously and returned. - * The API is designed to be compatible with the CommonJS AMD spec and - * RequireJS. - * @param {string[]|string} deps A name, or names for the payload - * @param {function|undefined} callback Function to call when the dependencies - * are resolved - * @return {undefined|object} The module required or undefined for - * array/callback method - */ - Domain.prototype.require = function(deps, callback) { - if (Array.isArray(deps)) { - var params = deps.map(function(dep) { - return this.lookup(dep); - }, this); - if (callback) { - callback.apply(null, params); - } - return undefined; - } - else { - return this.lookup(deps); - } - }; - - function normalize(path) { - var bits = path.split('/'); - var i = 1; - while (i < bits.length) { - if (bits[i] === '..') { - bits.splice(i-1, 1); - } else if (bits[i] === '.') { - bits.splice(i, 1); - } else { - i++; - } - } - return bits.join('/'); - } - - function join(a, b) { - a = a.trim(); - b = b.trim(); - if (/^\//.test(b)) { - return b; - } else { - return a.replace(/\/*$/, '/') + b; - } - } - - function dirname(path) { - var bits = path.split('/'); - bits.pop(); - return bits.join('/'); - } - - /** - * Lookup module names and resolve them by calling the definition function if - * needed. - * @param {string} moduleName A name for the payload to lookup - * @return {object} The module specified by aModuleName or null if not found. - */ - Domain.prototype.lookup = function(moduleName) { - if (/^\./.test(moduleName)) { - moduleName = normalize(join(dirname(this._currentModule), moduleName)); - } - - if (moduleName in this.modules) { - var module = this.modules[moduleName]; - return module; - } - - if (!(moduleName in define.modules)) { - throw new Error("Module not defined: " + moduleName); - } - - var module = define.modules[moduleName]; - - if (typeof module == "function") { - var exports = {}; - var previousModule = this._currentModule; - this._currentModule = moduleName; - module(this.require.bind(this), exports, { id: moduleName, uri: "" }); - this._currentModule = previousModule; - module = exports; - } - - // cache the resulting module object for next time - this.modules[moduleName] = module; - - return module; - }; - -}()); - -define.Domain = Domain; -define.globalDomain = new Domain(); -var require = define.globalDomain.require.bind(define.globalDomain); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) { - - var base64VLQ = require('./base64-vlq'); - var util = require('./util'); - var ArraySet = require('./array-set').ArraySet; - - /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: - * - * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. - */ - function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = []; - this._sourcesContents = null; - } - - SourceMapGenerator.prototype._version = 3; - - /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer - * - * @param aSourceMapConsumer The SourceMap. - */ - SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; - - if (mapping.source) { - newMapping.source = mapping.source; - if (sourceRoot) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; - - if (mapping.name) { - newMapping.name = mapping.name; - } - } - - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; - - /** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ - SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - this._validateMapping(generated, original, source, name); - - if (source && !this._sources.has(source)) { - this._sources.add(source); - } - - if (name && !this._names.has(name)) { - this._names.add(name); - } - - this._mappings.push({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; - - /** - * Set the source content for a source file. - */ - SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot) { - source = util.relative(this._sourceRoot, source); - } - - if (aSourceContent !== null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = {}; - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; - } - } - }; - - /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. - * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - */ - SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); - - // Find mappings for the "aSourceFile" - this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.originalLine) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source !== null) { - // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name !== null && mapping.name !== null) { - // Only use the identifier name if it's an identifier - // in both SourceMaps - mapping.name = original.name; - } - } - } - - var source = mapping.source; - if (source && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name && !newNames.has(name)) { - newNames.add(name); - } - - }, this); - this._sources = newSources; - this._names = newNames; - - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); - }; - - /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. - */ - SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; - } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; - } - else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); - } - }; - - /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. - */ - SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var mapping; - - // The mappings must be guaranteed to be in sorted order before we start - // serializing them or else the generated line numbers (which are defined - // via the ';' separators) will be all messed up. Note: it might be more - // performant to maintain the sorting as we insert them, rather than as we - // serialize them, but the big O is the same either way. - this._mappings.sort(util.compareByGeneratedPositions); - - for (var i = 0, len = this._mappings.length; i < len; i++) { - mapping = this._mappings[i]; - - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - result += ';'; - previousGeneratedLine++; - } - } - else { - if (i > 0) { - if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { - continue; - } - result += ','; - } - } - - result += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; - - if (mapping.source) { - result += base64VLQ.encode(this._sources.indexOf(mapping.source) - - previousSource); - previousSource = this._sources.indexOf(mapping.source); - - // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; - - result += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; - - if (mapping.name) { - result += base64VLQ.encode(this._names.indexOf(mapping.name) - - previousName); - previousName = this._names.indexOf(mapping.name); - } - } - } - - return result; - }; - - SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, - key) - ? this._sourcesContents[key] - : null; - }, this); - }; - - /** - * Externalize the source map. - */ - SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - file: this._file, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._sourceRoot) { - map.sourceRoot = this._sourceRoot; - } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); - } - - return map; - }; - - /** - * Render the source map being generated to a string. - */ - SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this); - }; - - exports.SourceMapGenerator = SourceMapGenerator; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) { - - var base64 = require('./base64'); - - // A single base 64 digit can contain 6 bits of data. For the base 64 variable - // length quantities we use in the source map spec, the first bit is the sign, - // the next four bits are the actual value, and the 6th bit is the - // continuation bit. The continuation bit tells us whether there are more - // digits in this value following this digit. - // - // Continuation - // | Sign - // | | - // V V - // 101011 - - var VLQ_BASE_SHIFT = 5; - - // binary: 100000 - var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - - // binary: 011111 - var VLQ_BASE_MASK = VLQ_BASE - 1; - - // binary: 100000 - var VLQ_CONTINUATION_BIT = VLQ_BASE; - - /** - * Converts 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) - */ - function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; - } - - /** - * Converts 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 - */ - function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; - } - - /** - * Returns the base 64 VLQ encoded value. - */ - exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); - - return encoded; - }; - - /** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string. - */ - exports.decode = function base64VLQ_decode(aStr) { - var i = 0; - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - do { - if (i >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } - digit = base64.decode(aStr.charAt(i++)); - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); - - return { - value: fromVLQSigned(result), - rest: aStr.slice(i) - }; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) { - - var charToIntMap = {}; - var intToCharMap = {}; - - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' - .split('') - .forEach(function (ch, index) { - charToIntMap[ch] = index; - intToCharMap[index] = ch; - }); - - /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. - */ - exports.encode = function base64_encode(aNumber) { - if (aNumber in intToCharMap) { - return intToCharMap[aNumber]; - } - throw new TypeError("Must be between 0 and 63: " + aNumber); - }; - - /** - * Decode a single base 64 digit to an integer. - */ - exports.decode = function base64_decode(aChar) { - if (aChar in charToIntMap) { - return charToIntMap[aChar]; - } - throw new TypeError("Not a valid base 64 digit: " + aChar); - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * This is a helper function for getting values from parameter/options - * objects. - * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. - */ - function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); - } - } - exports.getArg = getArg; - - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; - var dataUrlRegexp = /^data:.+\,.+/; - - function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; - } - return { - scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] - }; - } - exports.urlParse = urlParse; - - function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; - } - exports.urlGenerate = urlGenerate; - - function join(aRoot, aPath) { - var url; - - if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) { - return aPath; - } - - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); - } - - return aRoot.replace(/\/$/, '') + '/' + aPath; - } - exports.join = join; - - /** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ - function toSetString(aStr) { - return '$' + aStr; - } - exports.toSetString = toSetString; - - function fromSetString(aStr) { - return aStr.substr(1); - } - exports.fromSetString = fromSetString; - - function relative(aRoot, aPath) { - aRoot = aRoot.replace(/\/$/, ''); - - var url = urlParse(aRoot); - if (aPath.charAt(0) == "/" && url && url.path == "/") { - return aPath.slice(1); - } - - return aPath.indexOf(aRoot + '/') === 0 - ? aPath.substr(aRoot.length + 1) - : aPath; - } - exports.relative = relative; - - function strcmp(aStr1, aStr2) { - var s1 = aStr1 || ""; - var s2 = aStr2 || ""; - return (s1 > s2) - (s1 < s2); - } - - /** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ - function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp; - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp || onlyCompareOriginal) { - return cmp; - } - - cmp = strcmp(mappingA.name, mappingB.name); - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - return mappingA.generatedColumn - mappingB.generatedColumn; - }; - exports.compareByOriginalPositions = compareByOriginalPositions; - - /** - * Comparator between two mappings where the generated positions are - * compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ - function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { - var cmp; - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp || onlyCompareGenerated) { - return cmp; - } - - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp) { - return cmp; - } - - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp) { - return cmp; - } - - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp) { - return cmp; - } - - return strcmp(mappingA.name, mappingB.name); - }; - exports.compareByGeneratedPositions = compareByGeneratedPositions; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) { - - var util = require('./util'); - - /** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ - function ArraySet() { - this._array = []; - this._set = {}; - } - - /** - * Static method for creating ArraySet instances from an existing array. - */ - ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; - }; - - /** - * Add the given string to this set. - * - * @param String aStr - */ - ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var isDuplicate = this.has(aStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - this._set[util.toSetString(aStr)] = idx; - } - }; - - /** - * Is the given string a member of this set? - * - * @param String aStr - */ - ArraySet.prototype.has = function ArraySet_has(aStr) { - return Object.prototype.hasOwnProperty.call(this._set, - util.toSetString(aStr)); - }; - - /** - * What is the index of the given string in the array? - * - * @param String aStr - */ - ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (this.has(aStr)) { - return this._set[util.toSetString(aStr)]; - } - throw new Error('"' + aStr + '" is not in the set.'); - }; - - /** - * What is the element at the given index? - * - * @param Number aIdx - */ - ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; - } - throw new Error('No element indexed by ' + aIdx); - }; - - /** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ - ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); - }; - - exports.ArraySet = ArraySet; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) { - - var util = require('./util'); - var binarySearch = require('./binary-search'); - var ArraySet = require('./array-set').ArraySet; - var base64VLQ = require('./base64-vlq'); - - /** - * A SourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ - function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); - - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names, true); - this._sources = ArraySet.fromArray(sources, true); - - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; - } - - /** - * Create a SourceMapConsumer from a SourceMapGenerator. - * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns SourceMapConsumer - */ - SourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(SourceMapConsumer.prototype); - - smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; - - smc.__generatedMappings = aSourceMap._mappings.slice() - .sort(util.compareByGeneratedPositions); - smc.__originalMappings = aSourceMap._mappings.slice() - .sort(util.compareByOriginalPositions); - - return smc; - }; - - /** - * The version of the source mapping spec that we are consuming. - */ - SourceMapConsumer.prototype._version = 3; - - /** - * The list of original sources. - */ - Object.defineProperty(SourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; - }, this); - } - }); - - // `__generatedMappings` and `__originalMappings` are arrays that hold the - // parsed mapping coordinates from the source map's "mappings" attribute. They - // are lazily instantiated, accessed via the `_generatedMappings` and - // `_originalMappings` getters respectively, and we only parse the mappings - // and create these arrays once queried for a source location. We jump through - // these hoops because there can be many thousands of mappings, and parsing - // them is expensive, so we only want to do it if we must. - // - // Each object in the arrays is of the form: - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `_generatedMappings` is ordered by the generated positions. - // - // `_originalMappings` is ordered by the original positions. - - SourceMapConsumer.prototype.__generatedMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__generatedMappings; - } - }); - - SourceMapConsumer.prototype.__originalMappings = null; - Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this.__generatedMappings = []; - this.__originalMappings = []; - this._parseMappings(this._mappings, this.sourceRoot); - } - - return this.__originalMappings; - } - }); - - /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ - SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var mappingSeparator = /^[,;]/; - var str = aStr; - var mapping; - var temp; - - while (str.length > 0) { - if (str.charAt(0) === ';') { - generatedLine++; - str = str.slice(1); - previousGeneratedColumn = 0; - } - else if (str.charAt(0) === ',') { - str = str.slice(1); - } - else { - mapping = {}; - mapping.generatedLine = generatedLine; - - // Generated column. - temp = base64VLQ.decode(str); - mapping.generatedColumn = previousGeneratedColumn + temp.value; - previousGeneratedColumn = mapping.generatedColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original source. - temp = base64VLQ.decode(str); - mapping.source = this._sources.at(previousSource + temp.value); - previousSource += temp.value; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source, but no line and column'); - } - - // Original line. - temp = base64VLQ.decode(str); - mapping.originalLine = previousOriginalLine + temp.value; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; - str = temp.rest; - if (str.length === 0 || mappingSeparator.test(str.charAt(0))) { - throw new Error('Found a source and line, but no column'); - } - - // Original column. - temp = base64VLQ.decode(str); - mapping.originalColumn = previousOriginalColumn + temp.value; - previousOriginalColumn = mapping.originalColumn; - str = temp.rest; - - if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) { - // Original name. - temp = base64VLQ.decode(str); - mapping.name = this._names.at(previousName + temp.value); - previousName += temp.value; - str = temp.rest; - } - } - - this.__generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - this.__originalMappings.push(mapping); - } - } - } - - this.__originalMappings.sort(util.compareByOriginalPositions); - }; - - /** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ - SourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. - - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } - - return binarySearch.search(aNeedle, aMappings, aComparator); - }; - - /** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ - SourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var mapping = this._findMapping(needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositions); - - if (mapping) { - var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { - source = util.join(this.sourceRoot, source); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: util.getArg(mapping, 'name', null) - }; - } - - return { - source: null, - line: null, - column: null, - name: null - }; - }; - - /** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * availible. - */ - SourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource) { - if (!this.sourcesContent) { - return null; - } - - if (this.sourceRoot) { - aSource = util.relative(this.sourceRoot, aSource); - } - - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - - var url; - if (this.sourceRoot - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } - - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } - - throw new Error('"' + aSource + '" is not in the SourceMap.'); - }; - - /** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ - SourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - if (this.sourceRoot) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - - var mapping = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions); - - if (mapping) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null) - }; - } - - return { - line: null, - column: null - }; - }; - - SourceMapConsumer.GENERATED_ORDER = 1; - SourceMapConsumer.ORIGINAL_ORDER = 2; - - /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ - SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } - - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source; - if (source && sourceRoot) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name - }; - }).forEach(aCallback, context); - }; - - exports.SourceMapConsumer = SourceMapConsumer; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) { - - /** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - */ - function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the next - // closest element that is less than that element. - // - // 3. We did not find the exact element, and there is no next-closest - // element which is less than the one we are searching for, so we - // return null. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return aHaystack[mid]; - } - else if (cmp > 0) { - // aHaystack[mid] is greater than our needle. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare); - } - // We did not find an exact match, return the next closest one - // (termination case 2). - return aHaystack[mid]; - } - else { - // aHaystack[mid] is less than our needle. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare); - } - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (2) or (3) and return the appropriate thing. - return aLow < 0 - ? null - : aHaystack[aLow]; - } - } - - /** - * This is an implementation of binary search which will always try and return - * the next lowest value checked if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - */ - exports.search = function search(aNeedle, aHaystack, aCompare) { - return aHaystack.length > 0 - ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare) - : null; - }; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ -define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) { - - var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; - var util = require('./util'); - - /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. - */ - function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; - if (aChunks != null) this.add(aChunks); - } - - /** - * Creates a SourceNode from generated code and a SourceMapConsumer. - * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - */ - SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[0]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - } - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - node.setSourceContent(sourceFile, content); - } - }); - - return node; - - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - mapping.source, - code, - mapping.name)); - } - } - }; - - /** - * Add a chunk of generated JS to this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Add a chunk of generated JS to the beginning of this source node. - * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. - */ - SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk instanceof SourceNode || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; - }; - - /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk instanceof SourceNode) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } - }; - - /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. - * - * @param aSep The separator. - */ - SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; - }; - - /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. - */ - SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild instanceof SourceNode) { - lastChild.replaceRight(aPattern, aReplacement); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; - }; - - /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file - */ - SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; - - /** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. - * - * @param aFn The traversal function. - */ - SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i] instanceof SourceNode) { - this.children[i].walkSourceContents(aFn); - } - } - - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; - - /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ - SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; - }; - - /** - * Returns the string representation of this source node along with a source - * map. - */ - SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { - generated.line++; - generated.column = 0; - } else { - generated.column++; - } - }); - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; - }; - - exports.SourceNode = SourceNode; - -}); -/* -*- Mode: js; js-indent-level: 2; -*- */ -/////////////////////////////////////////////////////////////////////////////// - -this.sourceMap = { - SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, - SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, - SourceNode: require('source-map/source-node').SourceNode -}; - -// footer to wrap "source-map" module - return this.sourceMap; - }(); -})(); \ No newline at end of file diff --git a/dist/lessc-rhino-1.7.5.js b/dist/lessc-rhino-1.7.5.js deleted file mode 100644 index 55c62d54f..000000000 --- a/dist/lessc-rhino-1.7.5.js +++ /dev/null @@ -1,449 +0,0 @@ -/* Less.js v1.7.5 RHINO | Copyright (c) 2009-2014, Alexis Sellier */ - -/*global name:true, less, loadStyleSheet, os */ - -function formatError(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - -// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - var stylize = function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; -} - -function writeError(ctx, options) { - options = options || {}; - if (options.silent) { return; } - var message = formatError(ctx, options); - throw new Error(message); -} - -function loadStyleSheet(sheet, callback, reload, remaining) { - var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')), - sheetName = name.slice(0, endOfPath + 1) + sheet.href, - contents = sheet.contents || {}, - input = readFile(sheetName); - - input = input.replace(/^\xEF\xBB\xBF/, ''); - - contents[sheetName] = input; - - var parser = new less.Parser({ - paths: [sheet.href.replace(/[\w\.-]+$/, '')], - contents: contents - }); - parser.parse(input, function (e, root) { - if (e) { - return writeError(e); - } - try { - callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName); - } catch(e) { - writeError(e); - } - }); -} - -less.Parser.fileLoader = function (file, currentFileInfo, callback, env) { - - var href = file; - if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) { - href = less.modules.path.join(currentFileInfo.currentDirectory, file); - } - - var path = less.modules.path.dirname(href); - - var newFileInfo = { - currentDirectory: path + '/', - filename: href - }; - - if (currentFileInfo) { - newFileInfo.entryPath = currentFileInfo.entryPath; - newFileInfo.rootpath = currentFileInfo.rootpath; - newFileInfo.rootFilename = currentFileInfo.rootFilename; - newFileInfo.relativeUrls = currentFileInfo.relativeUrls; - } else { - newFileInfo.entryPath = path; - newFileInfo.rootpath = less.rootpath || path; - newFileInfo.rootFilename = href; - newFileInfo.relativeUrls = env.relativeUrls; - } - - var j = file.lastIndexOf('/'); - if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) { - var relativeSubDirectory = file.slice(0, j+1); - newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file - } - newFileInfo.currentDirectory = path; - newFileInfo.filename = href; - - var data = null; - try { - data = readFile(href); - } catch (e) { - callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" }); - return; - } - - try { - callback(null, data, href, newFileInfo, { lastModified: 0 }); - } catch (e) { - callback(e, null, href); - } -}; - - -function writeFile(filename, content) { - var fstream = new java.io.FileWriter(filename); - var out = new java.io.BufferedWriter(fstream); - out.write(content); - out.close(); -} - -// Command line integration via Rhino -(function (args) { - - var options = { - depends: false, - compress: false, - cleancss: false, - max_line_len: -1, - optimization: 1, - silent: false, - verbose: false, - lint: false, - paths: [], - color: true, - strictImports: false, - rootpath: '', - relativeUrls: false, - ieCompat: true, - strictMath: false, - strictUnits: false - }; - var continueProcessing = true, - currentErrorcode; - - var checkArgFunc = function(arg, option) { - if (!option) { - print(arg + " option requires a parameter"); - continueProcessing = false; - return false; - } - return true; - }; - - var checkBooleanArg = function(arg) { - var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg); - if (!onOff) { - print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no"); - continueProcessing = false; - return false; - } - return Boolean(onOff[2]); - }; - - var warningMessages = ""; - var sourceMapFileInline = false; - - args = args.filter(function (arg) { - var match = arg.match(/^-I(.+)$/); - - if (match) { - options.paths.push(match[1]); - return false; - } - - match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i); - if (match) { arg = match[1]; } // was (?:=([^\s]*)), check! - else { return arg; } - - switch (arg) { - case 'v': - case 'version': - console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]"); - continueProcessing = false; - break; - case 'verbose': - options.verbose = true; - break; - case 's': - case 'silent': - options.silent = true; - break; - case 'l': - case 'lint': - options.lint = true; - break; - case 'strict-imports': - options.strictImports = true; - break; - case 'h': - case 'help': - //TODO -// require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - break; - case 'x': - case 'compress': - options.compress = true; - break; - case 'M': - case 'depends': - options.depends = true; - break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. assuming clean-css."; - options.cleancss = true; - break; - case 'clean-css': - options.cleancss = true; - break; - case 'max-line-len': - if (checkArgFunc(arg, match[2])) { - options.maxLineLen = parseInt(match[2], 10); - if (options.maxLineLen <= 0) { - options.maxLineLen = -1; - } - } - break; - case 'no-color': - options.color = false; - break; - case 'no-ie-compat': - options.ieCompat = false; - break; - case 'no-js': - options.javascriptEnabled = false; - break; - case 'include-path': - if (checkArgFunc(arg, match[2])) { - options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':') - .map(function(p) { - if (p) { -// return path.resolve(process.cwd(), p); - return p; - } - }); - } - break; - case 'O0': options.optimization = 0; break; - case 'O1': options.optimization = 1; break; - case 'O2': options.optimization = 2; break; - case 'line-numbers': - if (checkArgFunc(arg, match[2])) { - options.dumpLineNumbers = match[2]; - } - break; - case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; - } - break; - case 'source-map-rootpath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; - } - break; - case 'source-map-basepath': - if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; - } - break; - case 'source-map-map-inline': - sourceMapFileInline = true; - options.sourceMap = true; - break; - case 'source-map-less-inline': - options.outputSourceFiles = true; - break; - case 'source-map-url': - if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; - } - break; - case 'source-map-output-map-file': - if (checkArgFunc(arg, match[2])) { - options.writeSourceMap = function(sourceMapContent) { - writeFile(match[2], sourceMapContent); - }; - } - break; - case 'rp': - case 'rootpath': - if (checkArgFunc(arg, match[2])) { - options.rootpath = match[2].replace(/\\/g, '/'); - } - break; - case "ru": - case "relative-urls": - options.relativeUrls = true; - break; - case "sm": - case "strict-math": - if (checkArgFunc(arg, match[2])) { - options.strictMath = checkBooleanArg(match[2]); - } - break; - case "su": - case "strict-units": - if (checkArgFunc(arg, match[2])) { - options.strictUnits = checkBooleanArg(match[2]); - } - break; - default: - console.log('invalid option ' + arg); - continueProcessing = false; - } - }); - - if (!continueProcessing) { - return; - } - - var name = args[0]; - if (name && name != '-') { -// name = path.resolve(process.cwd(), name); - } - var output = args[1]; - var outputbase = args[1]; - if (output) { - options.sourceMapOutputFilename = output; -// output = path.resolve(process.cwd(), output); - if (warningMessages) { - console.log(warningMessages); - } - } - -// options.sourceMapBasepath = process.cwd(); -// options.sourceMapBasepath = ''; - - if (options.sourceMap === true) { - console.log("output: " + output); - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; - } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename); - } else if (options.sourceMap) { - options.sourceMapOutputFilename = options.sourceMap; - } - - - if (!name) { - console.log("lessc: no inout files"); - console.log(""); - // TODO -// require('../lib/less/lessc_helper').printUsage(); - currentErrorcode = 1; - return; - } - -// var ensureDirectory = function (filepath) { -// var dir = path.dirname(filepath), -// cmd, -// existsSync = fs.existsSync || path.existsSync; -// if (!existsSync(dir)) { -// if (mkdirp === undefined) { -// try {mkdirp = require('mkdirp');} -// catch(e) { mkdirp = null; } -// } -// cmd = mkdirp && mkdirp.sync || fs.mkdirSync; -// cmd(dir); -// } -// }; - - if (options.depends) { - if (!outputbase) { - console.log("option --depends requires an output path to be specified"); - return; - } - console.log(outputbase + ": "); - } - - if (!name) { - console.log('No files present in the fileset'); - quit(1); - } - - var input = null; - try { - input = readFile(name, 'utf-8'); - - } catch (e) { - console.log('lesscss: couldn\'t open file ' + name); - quit(1); - } - - options.filename = name; - var result; - try { - var parser = new less.Parser(options); - parser.parse(input, function (e, root) { - if (e) { - writeError(e, options); - quit(1); - } else { - result = root.toCSS(options); - if (output) { - writeFile(output, result); - console.log("Written to " + output); - } else { - print(result); - } - quit(0); - } - }); - } - catch(e) { - writeError(e, options); - quit(1); - } -}(arguments)); From bc42a738868c1f48f71bf3899bbdb20b8e3dbf82 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 6 Sep 2014 17:23:19 +0100 Subject: [PATCH 081/161] Fix memory leak in error. Fixes #2025 --- lib/less/less-error.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/less/less-error.js b/lib/less/less-error.js index e21bd5712..6978116d2 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -2,6 +2,8 @@ var utils = require("./utils.js"); var LessError = module.exports = function LessError(e, importManager, currentFilename) { + Error.call(this); + var filename = e.filename || currentFilename; if (importManager && filename) { @@ -29,5 +31,5 @@ var LessError = module.exports = function LessError(e, importManager, currentFil this.stack = e.stack; }; -LessError.prototype = new Error(); +LessError.prototype = Object.create(Error.prototype); LessError.prototype.constructor = LessError; From 50e299b8bc2b8100c52c77f37da01e2f81ae4ca5 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 7 Sep 2014 14:40:28 +0100 Subject: [PATCH 082/161] Fix url import. Fixes #2089. --- lib/less/tree/import.js | 6 +++++- test/css/import.css | 3 +++ test/less/import/deeper/deeper-2/url-import-2.less | 3 +++ test/less/import/deeper/deeper-2/url-import.less | 1 + test/less/import/deeper/url-import.less | 1 + test/less/import/import-test-a.less | 1 + 6 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/less/import/deeper/deeper-2/url-import-2.less create mode 100644 test/less/import/deeper/deeper-2/url-import.less create mode 100644 test/less/import/deeper/url-import.less diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index c0d045931..6b5f26fb7 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -75,7 +75,11 @@ Import.prototype.getPath = function () { return null; }; Import.prototype.evalForImport = function (env) { - return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); + var path = this.path; + if (path instanceof URL) { + path = path.value; + } + return new(Import)(path.eval(env), this.features, this.options, this.index, this.currentFileInfo); }; Import.prototype.evalPath = function (env) { var path = this.path.eval(env); diff --git a/test/css/import.css b/test/css/import.css index 4c66eece5..f52f90823 100644 --- a/test/css/import.css +++ b/test/css/import.css @@ -20,6 +20,9 @@ height: 10px; color: red; } +.deep-import-url { + color: red; +} @media screen and (max-width: 601px) { #css { color: yellow; diff --git a/test/less/import/deeper/deeper-2/url-import-2.less b/test/less/import/deeper/deeper-2/url-import-2.less new file mode 100644 index 000000000..727ea57a5 --- /dev/null +++ b/test/less/import/deeper/deeper-2/url-import-2.less @@ -0,0 +1,3 @@ +.deep-import-url { + color: red; +} \ No newline at end of file diff --git a/test/less/import/deeper/deeper-2/url-import.less b/test/less/import/deeper/deeper-2/url-import.less new file mode 100644 index 000000000..2ec87bc22 --- /dev/null +++ b/test/less/import/deeper/deeper-2/url-import.less @@ -0,0 +1 @@ +@import url("url-import-2.less"); \ No newline at end of file diff --git a/test/less/import/deeper/url-import.less b/test/less/import/deeper/url-import.less new file mode 100644 index 000000000..003b07781 --- /dev/null +++ b/test/less/import/deeper/url-import.less @@ -0,0 +1 @@ +@import url("deeper-2/url-import.less"); \ No newline at end of file diff --git a/test/less/import/import-test-a.less b/test/less/import/import-test-a.less index b3b3b8fc8..1fa71c6ac 100644 --- a/test/less/import/import-test-a.less +++ b/test/less/import/import-test-a.less @@ -1,3 +1,4 @@ @import "import-test-b.less"; +@import url("deeper/url-import.less"); @a: 20%; @import "urls.less"; \ No newline at end of file From 2778f532667fbc271ce449436dc9f98802913b78 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 7 Sep 2014 15:07:45 +0100 Subject: [PATCH 083/161] Point at the node version of less when imported and expose the environment non-specific one --- lib/less-node/index.js | 8 ++++++-- package.json | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/less-node/index.js b/lib/less-node/index.js index d624dbabe..7b8f5c062 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -1,5 +1,9 @@ -var environment = require("./environment.js"); -var less = require("../less/index.js")(environment); +var environment = require("./environment.js"), + createLess = require("../less/index.js"), + less = createLess(environment); + +// allow people to create less with their own environment +less.createLess = createLess; less.formatError = function(ctx, options) { options = options || {}; diff --git a/package.json b/package.json index b2c383f04..ff44cfb7b 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "bin": { "lessc": "./bin/lessc" }, - "main": "./lib/less/index", + "main": "./lib/less-node/index", "directories": { "test": "./test" }, From 1a78cd5901ece46c68f1baf739f6c7602231837f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 8 Sep 2014 00:57:50 +0100 Subject: [PATCH 084/161] Remove unnecessary text from require statements --- lib/less-browser/index.js | 4 ++-- lib/less-node/index.js | 8 ++++---- lib/less/data/index.js | 4 ++-- lib/less/functions/color-blending.js | 4 ++-- lib/less/functions/color.js | 10 +++++----- lib/less/functions/data-uri.js | 6 +++--- lib/less/functions/default.js | 4 ++-- lib/less/functions/function-caller.js | 2 +- lib/less/functions/index.js | 22 +++++++++++----------- lib/less/functions/math.js | 4 ++-- lib/less/functions/number.js | 6 +++--- lib/less/functions/string.js | 8 ++++---- lib/less/functions/svg.js | 10 +++++----- lib/less/functions/types.js | 16 ++++++++-------- lib/less/imports.js | 4 ++-- lib/less/index.js | 16 ++++++++-------- lib/less/less-error.js | 2 +- lib/less/parse-tree.js | 4 ++-- lib/less/parser/parser-input.js | 4 ++-- lib/less/parser/parser.js | 10 +++++----- lib/less/render.js | 8 ++++---- lib/less/transform-tree.js | 6 +++--- lib/less/tree/alpha.js | 2 +- lib/less/tree/anonymous.js | 2 +- lib/less/tree/assignment.js | 2 +- lib/less/tree/attribute.js | 2 +- lib/less/tree/call.js | 4 ++-- lib/less/tree/color.js | 6 +++--- lib/less/tree/combinator.js | 2 +- lib/less/tree/comment.js | 4 ++-- lib/less/tree/condition.js | 2 +- lib/less/tree/detached-ruleset.js | 4 ++-- lib/less/tree/dimension.js | 8 ++++---- lib/less/tree/directive.js | 4 ++-- lib/less/tree/element.js | 8 ++++---- lib/less/tree/expression.js | 6 +++--- lib/less/tree/extend.js | 2 +- lib/less/tree/import.js | 12 ++++++------ lib/less/tree/javascript.js | 8 ++++---- lib/less/tree/js-eval-node.js | 4 ++-- lib/less/tree/keyword.js | 2 +- lib/less/tree/media.js | 14 +++++++------- lib/less/tree/mixin-call.js | 8 ++++---- lib/less/tree/mixin-definition.js | 12 ++++++------ lib/less/tree/negative.js | 6 +++--- lib/less/tree/operation.js | 6 +++--- lib/less/tree/paren.js | 2 +- lib/less/tree/quoted.js | 6 +++--- lib/less/tree/rule.js | 6 +++--- lib/less/tree/ruleset-call.js | 4 ++-- lib/less/tree/ruleset.js | 14 +++++++------- lib/less/tree/selector.js | 2 +- lib/less/tree/unicode-descriptor.js | 2 +- lib/less/tree/unit.js | 4 ++-- lib/less/tree/url.js | 2 +- lib/less/tree/value.js | 2 +- lib/less/tree/variable.js | 2 +- 57 files changed, 169 insertions(+), 169 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 7b7154838..ba43b44e4 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -29,9 +29,9 @@ function log(str, level) { var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), options = window.less, - environment = require("./environment.js")(options, isFileProtocol, log, logLevel); + environment = require("./environment")(options, isFileProtocol, log, logLevel); -window.less = less = require('../less/index.js')(environment); +window.less = less = require('../less')(environment); less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || diff --git a/lib/less-node/index.js b/lib/less-node/index.js index 7b8f5c062..c4124f90a 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -1,9 +1,9 @@ -var environment = require("./environment.js"), - createLess = require("../less/index.js"), - less = createLess(environment); +var environment = require("./environment"), + createFromEnvironment = require("../less"), + less = createFromEnvironment(environment); // allow people to create less with their own environment -less.createLess = createLess; +less.createFromEnvironment = createFromEnvironment; less.formatError = function(ctx, options) { options = options || {}; diff --git a/lib/less/data/index.js b/lib/less/data/index.js index 436caae88..a5f41e2ed 100644 --- a/lib/less/data/index.js +++ b/lib/less/data/index.js @@ -1,4 +1,4 @@ module.exports = { - colors: require("./colors.js"), - unitConversions: require("./unit-conversions.js") + colors: require("./colors"), + unitConversions: require("./unit-conversions") }; diff --git a/lib/less/functions/color-blending.js b/lib/less/functions/color-blending.js index ed2fa071f..537045108 100644 --- a/lib/less/functions/color-blending.js +++ b/lib/less/functions/color-blending.js @@ -1,5 +1,5 @@ -var Color = require("../tree/color.js"), - functionRegistry = require("./function-registry.js"); +var Color = require("../tree/color"), + functionRegistry = require("./function-registry"); // Color Blending // ref: http://www.w3.org/TR/compositing-1 diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js index c08aee8c6..ebdd042a5 100644 --- a/lib/less/functions/color.js +++ b/lib/less/functions/color.js @@ -1,8 +1,8 @@ -var Dimension = require("../tree/dimension.js"), - Color = require("../tree/color.js"), - Quoted = require("../tree/quoted.js"), - Anonymous = require("../tree/anonymous.js"), - functionRegistry = require("./function-registry.js"), +var Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + functionRegistry = require("./function-registry"), colorFunctions; function clamp(val) { diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index ad5ac6542..03f086cca 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -1,7 +1,7 @@ module.exports = function(environment) { - var Anonymous = require("../tree/anonymous.js"), - URL = require("../tree/url.js"), - functionRegistry = require("./function-registry.js"); + var Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + functionRegistry = require("./function-registry"); functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { diff --git a/lib/less/functions/default.js b/lib/less/functions/default.js index 8b97e7aa5..7e11bee83 100644 --- a/lib/less/functions/default.js +++ b/lib/less/functions/default.js @@ -1,5 +1,5 @@ -var Keyword = require("../tree/keyword.js"), - functionRegistry = require("./function-registry.js"); +var Keyword = require("../tree/keyword"), + functionRegistry = require("./function-registry"); var defaultFunc = { eval: function () { diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js index 753c14802..056b817d4 100644 --- a/lib/less/functions/function-caller.js +++ b/lib/less/functions/function-caller.js @@ -1,4 +1,4 @@ -var functionRegistry = require("./function-registry.js"); +var functionRegistry = require("./function-registry"); var functionCaller = function(name, env, currentFileInfo) { this.name = name.toLowerCase(); diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js index ab605b6ca..90703c0ee 100644 --- a/lib/less/functions/index.js +++ b/lib/less/functions/index.js @@ -1,19 +1,19 @@ module.exports = function(environment) { var functions = { - functionRegistry: require("./function-registry.js"), - functionCaller: require("./function-caller.js") + functionRegistry: require("./function-registry"), + functionCaller: require("./function-caller") }; //register functions - require("./default.js"); - require("./color.js"); - require("./color-blending.js"); - require("./data-uri.js")(environment); - require("./math.js"); - require("./number.js"); - require("./string.js"); - require("./svg.js")(environment); - require("./types.js"); + require("./default"); + require("./color"); + require("./color-blending"); + require("./data-uri")(environment); + require("./math"); + require("./number"); + require("./string"); + require("./svg")(environment); + require("./types"); return functions; }; diff --git a/lib/less/functions/math.js b/lib/less/functions/math.js index 947427370..b345fc392 100644 --- a/lib/less/functions/math.js +++ b/lib/less/functions/math.js @@ -1,5 +1,5 @@ -var Dimension = require("../tree/dimension.js"), - functionRegistry = require("./function-registry.js"); +var Dimension = require("../tree/dimension"), + functionRegistry = require("./function-registry"); var mathFunctions = { // name, unit diff --git a/lib/less/functions/number.js b/lib/less/functions/number.js index 0eb84c9ca..38d12f581 100644 --- a/lib/less/functions/number.js +++ b/lib/less/functions/number.js @@ -1,6 +1,6 @@ -var Dimension = require("../tree/dimension.js"), - Anonymous = require("../tree/anonymous.js"), - functionRegistry = require("./function-registry.js"); +var Dimension = require("../tree/dimension"), + Anonymous = require("../tree/anonymous"), + functionRegistry = require("./function-registry"); var minMax = function (isMin, args) { args = Array.prototype.slice.call(args); diff --git a/lib/less/functions/string.js b/lib/less/functions/string.js index a9547274e..efeb81005 100644 --- a/lib/less/functions/string.js +++ b/lib/less/functions/string.js @@ -1,7 +1,7 @@ -var Quoted = require("../tree/quoted.js"), - Anonymous = require("../tree/anonymous.js"), - JavaScript = require("../tree/javascript.js"), - functionRegistry = require("./function-registry.js"); +var Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + JavaScript = require("../tree/javascript"), + functionRegistry = require("./function-registry"); functionRegistry.addMultiple({ e: function (str) { diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js index 2927f0b34..27294e0f2 100644 --- a/lib/less/functions/svg.js +++ b/lib/less/functions/svg.js @@ -1,9 +1,9 @@ module.exports = function(environment) { - var Dimension = require("../tree/dimension.js"), - Color = require("../tree/color.js"), - Anonymous = require("../tree/anonymous.js"), - URL = require("../tree/url.js"), - functionRegistry = require("./function-registry.js"); + var Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + functionRegistry = require("./function-registry"); functionRegistry.add("svg-gradient", function(direction) { diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js index dfb51b664..2870f0331 100644 --- a/lib/less/functions/types.js +++ b/lib/less/functions/types.js @@ -1,11 +1,11 @@ -var Keyword = require("../tree/keyword.js"), - Dimension = require("../tree/dimension.js"), - Color = require("../tree/color.js"), - Quoted = require("../tree/quoted.js"), - Anonymous = require("../tree/anonymous.js"), - URL = require("../tree/url.js"), - Operation = require("../tree/operation.js"), - functionRegistry = require("./function-registry.js"); +var Keyword = require("../tree/keyword"), + Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + Operation = require("../tree/operation"), + functionRegistry = require("./function-registry"); var isa = function (n, Type) { return (n instanceof Type) ? Keyword.True : Keyword.False; diff --git a/lib/less/imports.js b/lib/less/imports.js index d0e149d13..3cb9cfb2b 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -1,5 +1,5 @@ -var contexts = require("./contexts.js"), - Parser = require('./parser/parser.js'); +var contexts = require("./contexts"), + Parser = require('./parser/parser'); var getImportManager = module.exports = function(environment, env) { var rootFilename = env && env.filename; diff --git a/lib/less/index.js b/lib/less/index.js index 2b2ed2bed..bd2ea8144 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,15 +1,15 @@ module.exports = function(environment) { var less = { version: [2, 0, 0], - data: require('./data/index.js'), - tree: require('./tree/index.js'), - visitor: require('./visitor/index.js'), - Parser: require('./parser/parser.js'), - functions: require('./functions/index.js')(environment), - contexts: require("./contexts.js"), - environment: environment + data: require('./data'), + tree: require('./tree'), + visitor: require('./visitor'), + Parser: require('./parser/parser'), + functions: require('./functions')(environment), + contexts: require("./contexts"), + environment: environment, + render: require("./render")(environment) }; - less.render = require("./render.js")(environment); return less; }; diff --git a/lib/less/less-error.js b/lib/less/less-error.js index 6978116d2..4332bb339 100644 --- a/lib/less/less-error.js +++ b/lib/less/less-error.js @@ -1,4 +1,4 @@ -var utils = require("./utils.js"); +var utils = require("./utils"); var LessError = module.exports = function LessError(e, importManager, currentFilename) { diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index 50f5f3b32..1ae303ff1 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -1,5 +1,5 @@ -var LessError = require('./less-error.js'), - transformTree = require("./transform-tree.js"); +var LessError = require('./less-error'), + transformTree = require("./transform-tree"); module.exports = function(environment) { diff --git a/lib/less/parser/parser-input.js b/lib/less/parser/parser-input.js index 5c799c624..fc0bf56b3 100644 --- a/lib/less/parser/parser-input.js +++ b/lib/less/parser/parser-input.js @@ -1,5 +1,5 @@ -var chunker = require('./chunker.js'), - LessError = require('../less-error.js'); +var chunker = require('./chunker'), + LessError = require('../less-error'); module.exports = function() { var input, // LeSS input string j, // current chunk diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 5f513e153..3246e5f3a 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -1,8 +1,8 @@ -var LessError = require('../less-error.js'), - tree = require("../tree/index.js"), - visitor = require("../visitor/index.js"), - getParserInput = require("./parser-input.js"), - utils = require("../utils.js"); +var LessError = require('../less-error'), + tree = require("../tree"), + visitor = require("../visitor"), + getParserInput = require("./parser-input"), + utils = require("../utils"); // // less.js - parser diff --git a/lib/less/render.js b/lib/less/render.js index 83f909860..16912075d 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,10 +1,10 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, - contexts = require("./contexts.js"), - getImportManager = require("./imports.js"), - Parser = require('./parser/parser.js'); + contexts = require("./contexts"), + getImportManager = require("./imports"), + Parser = require('./parser/parser'); var render = function(environment) { - var ParseTree = require("./parse-tree.js")(environment); + var ParseTree = require("./parse-tree")(environment); return function (input, options, callback) { options = options || {}; diff --git a/lib/less/transform-tree.js b/lib/less/transform-tree.js index bbfba9e98..cd7453905 100644 --- a/lib/less/transform-tree.js +++ b/lib/less/transform-tree.js @@ -1,6 +1,6 @@ -var contexts = require("./contexts.js"), - visitor = require("./visitor/index.js"), - tree = require("./tree/index.js"); +var contexts = require("./contexts"), + visitor = require("./visitor"), + tree = require("./tree"); module.exports = function(root, options) { options = options || {}; diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js index c4a7dd118..9d4d5fa63 100644 --- a/lib/less/tree/alpha.js +++ b/lib/less/tree/alpha.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Alpha = function (val) { this.value = val; diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index d68d5dfa6..0d81a3f58 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { this.value = value; diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index efcc9030d..c9492ce6a 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Assignment = function (key, val) { this.key = key; diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js index 4d713f2bc..67f336e4f 100644 --- a/lib/less/tree/attribute.js +++ b/lib/less/tree/attribute.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Attribute = function (key, op, value) { this.key = key; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 652194995..38bcbc0d0 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - FunctionCaller = require("../functions/function-caller.js"); +var Node = require("./node"), + FunctionCaller = require("../functions/function-caller"); // // A function call node. // diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 1de5809ec..c51adf1ea 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - colors = require("../data/colors.js"); +var Node = require("./node"), + colors = require("../data/colors"); // // RGB Colors - #ff0014, #eee @@ -162,7 +162,7 @@ Color.prototype.toARGB = function () { return toHex([this.alpha * 255].concat(this.rgb)); }; Color.prototype.compare = function (x) { - return (x.rgb && + return (x.rgb && x.rgb[0] === this.rgb[0] && x.rgb[1] === this.rgb[1] && x.rgb[2] === this.rgb[2] && diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js index ba0abb093..784933c65 100644 --- a/lib/less/tree/combinator.js +++ b/lib/less/tree/combinator.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Combinator = function (value) { if (value === ' ') { diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index 42262319f..f0989709f 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - getDebugInfo = require("./debug-info.js"); +var Node = require("./node"), + getDebugInfo = require("./debug-info"); var Comment = function (value, isLineComment, index, currentFileInfo) { this.value = value; diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 27b3be529..ee65e8734 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Condition = function (op, l, r, i, negate) { this.op = op.trim(); diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js index 4c7723f49..f0c81e7c3 100644 --- a/lib/less/tree/detached-ruleset.js +++ b/lib/less/tree/detached-ruleset.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - contexts = require("../contexts.js"); +var Node = require("./node"), + contexts = require("../contexts"); var DetachedRuleset = function (ruleset, frames) { this.ruleset = ruleset; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index dfd00cfad..ce59f2716 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -1,7 +1,7 @@ -var Node = require("./node.js"), - unitConversions = require("../data/unit-conversions.js"), - Unit = require("./unit.js"), - Color = require("./color.js"); +var Node = require("./node"), + unitConversions = require("../data/unit-conversions"), + Unit = require("./unit"), + Color = require("./color"); // // A number with a unit diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index 2f99d3f18..b39df719b 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - Ruleset = require("./ruleset.js"); +var Node = require("./node"), + Ruleset = require("./ruleset"); var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { this.name = name; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index cc1c83ec0..f62f65614 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -1,6 +1,6 @@ -var Node = require("./node.js"), - Paren = require("./paren.js"), - Combinator = require("./combinator.js"); +var Node = require("./node"), + Paren = require("./paren"), + Combinator = require("./combinator"); var Element = function (combinator, value, index, currentFileInfo) { this.combinator = combinator instanceof Combinator ? @@ -38,7 +38,7 @@ Element.prototype.toCSS = function (env) { env = env || {}; var value = this.value, firstSelector = env.firstSelector; if (value instanceof Paren) { - // selector in parens should not be affected by outer selector + // selector in parens should not be affected by outer selector // flags (breaks only interpolated selectors - see #1973) env.firstSelector = true; } diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 11d09cb22..2f25c7ef2 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -1,6 +1,6 @@ -var Node = require("./node.js"), - Paren = require("./paren.js"), - Comment = require("./comment.js"); +var Node = require("./node"), + Paren = require("./paren"), + Comment = require("./comment"); var Expression = function (value) { this.value = value; }; Expression.prototype = new Node(); diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js index d4d0676be..caa7016fc 100644 --- a/lib/less/tree/extend.js +++ b/lib/less/tree/extend.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Extend = function Extend(selector, option, index) { this.selector = selector; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 6b5f26fb7..e0ac6d81d 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -1,9 +1,9 @@ -var Node = require("./node.js"), - Media = require("./media.js"), - URL = require("./url.js"), - Quoted = require("./quoted.js"), - Ruleset = require("./ruleset.js"), - Anonymous = require("./anonymous.js"); +var Node = require("./node"), + Media = require("./media"), + URL = require("./url"), + Quoted = require("./quoted"), + Ruleset = require("./ruleset"), + Anonymous = require("./anonymous"); // // CSS @import node diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index 4b5a2c07d..05913b8b2 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -1,7 +1,7 @@ -var JsEvalNode = require("./js-eval-node.js"), - Dimension = require("./dimension.js"), - Quoted = require("./quoted.js"), - Anonymous = require("./anonymous.js"); +var JsEvalNode = require("./js-eval-node"), + Dimension = require("./dimension"), + Quoted = require("./quoted"), + Anonymous = require("./anonymous"); var JavaScript = function (string, escaped, index, currentFileInfo) { this.escaped = escaped; diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js index 297a7a191..2df63bb42 100644 --- a/lib/less/tree/js-eval-node.js +++ b/lib/less/tree/js-eval-node.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - Variable = require("./variable.js"); +var Node = require("./node"), + Variable = require("./variable"); var JsEvalNode = function() { }; diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index c8006920b..80677a468 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Keyword = function (value) { this.value = value; }; Keyword.prototype = new Node(); diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 5926b324a..45d008d9b 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -1,10 +1,10 @@ -var Ruleset = require("./ruleset.js"), - Value = require("./value.js"), - Element = require("./element.js"), - Selector = require("./selector.js"), - Anonymous = require("./anonymous.js"), - Expression = require("./expression.js"), - Directive = require("./directive.js"); +var Ruleset = require("./ruleset"), + Value = require("./value"), + Element = require("./element"), + Selector = require("./selector"), + Anonymous = require("./anonymous"), + Expression = require("./expression"), + Directive = require("./directive"); var Media = function (value, features, index, currentFileInfo) { this.index = index; diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js index 358dd2cdd..7def79c9c 100644 --- a/lib/less/tree/mixin-call.js +++ b/lib/less/tree/mixin-call.js @@ -1,7 +1,7 @@ -var Node = require("./node.js"), - Selector = require("./selector.js"), - MixinDefinition = require("./mixin-definition.js"), - defaultFunc = require("../functions/default.js"); +var Node = require("./node"), + Selector = require("./selector"), + MixinDefinition = require("./mixin-definition"), + defaultFunc = require("../functions/default"); var MixinCall = function (elements, args, index, currentFileInfo, important) { this.selector = new(Selector)(elements); diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js index 3c2f1950f..e7a895776 100644 --- a/lib/less/tree/mixin-definition.js +++ b/lib/less/tree/mixin-definition.js @@ -1,9 +1,9 @@ -var Selector = require("./selector.js"), - Element = require("./element.js"), - Ruleset = require("./ruleset.js"), - Rule = require("./rule.js"), - Expression = require("./expression.js"), - contexts = require("../contexts.js"); +var Selector = require("./selector"), + Element = require("./element"), + Ruleset = require("./ruleset"), + Rule = require("./rule"), + Expression = require("./expression"), + contexts = require("../contexts"); var Definition = function (name, params, rules, condition, variadic, frames) { this.name = name; diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index d33a31579..423293012 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -1,6 +1,6 @@ -var Node = require("./node.js"), - Operation = require("./operation.js"), - Dimension = require("./dimension.js"); +var Node = require("./node"), + Operation = require("./operation"), + Dimension = require("./dimension"); var Negative = function (node) { this.value = node; diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index 6b558e2c5..a76dda9de 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -1,6 +1,6 @@ -var Node = require("./node.js"), - Color = require("./color.js"), - Dimension = require("./dimension.js"); +var Node = require("./node"), + Color = require("./color"), + Dimension = require("./dimension"); var Operation = function (op, operands, isSpaced) { this.op = op.trim(); diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index ab9b22aa4..54c71fbe0 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Paren = function (node) { this.value = node; diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 79c37f7ee..11e646b40 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -1,6 +1,6 @@ -var Node = require("./node.js"), - JsEvalNode = require("./js-eval-node.js"), - Variable = require("./variable.js"); +var Node = require("./node"), + JsEvalNode = require("./js-eval-node"), + Variable = require("./variable"); var Quoted = function (str, content, escaped, index, currentFileInfo) { this.escaped = escaped; diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index 521a2f08a..fcc53295f 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -1,6 +1,6 @@ -var Node = require("./node.js"), - Value = require("./value.js"), - Keyword = require("./keyword.js"); +var Node = require("./node"), + Value = require("./value"), + Keyword = require("./keyword"); var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) { this.name = name; diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js index 537a97836..3a2e77a40 100644 --- a/lib/less/tree/ruleset-call.js +++ b/lib/less/tree/ruleset-call.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - Variable = require("./variable.js"); +var Node = require("./node"), + Variable = require("./variable"); var RulesetCall = function (variable) { this.variable = variable; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 6e4e690e6..0760595b2 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -1,10 +1,10 @@ -var Node = require("./node.js"), - Rule = require("./rule.js"), - Selector = require("./selector.js"), - Element = require("./element.js"), - contexts = require("../contexts.js"), - defaultFunc = require("../functions/default.js"), - getDebugInfo = require("./debug-info.js"); +var Node = require("./node"), + Rule = require("./rule"), + Selector = require("./selector"), + Element = require("./element"), + contexts = require("../contexts"), + defaultFunc = require("../functions/default"), + getDebugInfo = require("./debug-info"); var Ruleset = function (selectors, rules, strictImports) { this.selectors = selectors; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index ef39ece1b..e27e70f01 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { this.elements = elements; diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js index 72f33ea80..ff87b54d4 100644 --- a/lib/less/tree/unicode-descriptor.js +++ b/lib/less/tree/unicode-descriptor.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var UnicodeDescriptor = function (value) { this.value = value; diff --git a/lib/less/tree/unit.js b/lib/less/tree/unit.js index 64b6b4fe2..d9a74598b 100644 --- a/lib/less/tree/unit.js +++ b/lib/less/tree/unit.js @@ -1,5 +1,5 @@ -var Node = require("./node.js"), - unitConversions = require("../data/unit-conversions.js"); +var Node = require("./node"), + unitConversions = require("../data/unit-conversions"); var Unit = function (numerator, denominator, backupUnit) { this.numerator = numerator ? numerator.slice(0).sort() : []; diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 603e4b71c..6dd2fa6f9 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var URL = function (val, currentFileInfo, isEvald) { this.value = val; diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index fb1c7f850..b550edd0f 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Value = function (value) { this.value = value; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index cff740ef4..87428af5d 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -1,4 +1,4 @@ -var Node = require("./node.js"); +var Node = require("./node"); var Variable = function (name, index, currentFileInfo) { this.name = name; From e357dae7bc24faf311e07340069a8e31ec3a6b41 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 9 Sep 2014 21:20:04 +0100 Subject: [PATCH 085/161] allow lessc to load plugins. rename visitor directory to visitors to match others. Fix indexes in URL --- bin/lessc | 14 ++++++--- lib/less-node/index.js | 8 +++-- lib/less-node/node-plugin-manager.js | 29 +++++++++++++++++++ lib/less/functions/data-uri.js | 6 ++-- lib/less/functions/function-caller.js | 3 +- lib/less/functions/svg.js | 2 +- lib/less/index.js | 11 +++++-- lib/less/parser/parser.js | 10 +++---- lib/less/plugin-manager.js | 17 +++++++++++ lib/less/transform-tree.js | 16 +++++----- lib/less/tree/call.js | 2 +- lib/less/tree/url.js | 5 ++-- lib/less/visitor/index.js | 9 ------ .../{visitor => visitors}/extend-visitor.js | 4 +-- .../{visitor => visitors}/import-visitor.js | 4 +-- lib/less/visitors/index.js | 9 ++++++ .../join-selector-visitor.js | 2 +- .../{visitor => visitors}/to-css-visitor.js | 4 +-- lib/less/{visitor => visitors}/visitor.js | 2 +- 19 files changed, 111 insertions(+), 46 deletions(-) create mode 100644 lib/less-node/node-plugin-manager.js create mode 100644 lib/less/plugin-manager.js delete mode 100644 lib/less/visitor/index.js rename lib/less/{visitor => visitors}/extend-visitor.js (99%) rename lib/less/{visitor => visitors}/import-visitor.js (98%) create mode 100644 lib/less/visitors/index.js rename lib/less/{visitor => visitors}/join-selector-visitor.js (97%) rename lib/less/{visitor => visitors}/to-css-visitor.js (99%) rename lib/less/{visitor => visitors}/visitor.js (98%) diff --git a/bin/lessc b/bin/lessc index d8e195366..50fc18f2d 100755 --- a/bin/lessc +++ b/bin/lessc @@ -5,7 +5,9 @@ var path = require('path'), os = require('os'), mkdirp; -var less = require('../lib/less-node'); +var less = require('../lib/less-node'), + pluginManager = new less.PluginManager(less); + var args = process.argv.slice(1); var options = { depends: false, @@ -251,9 +253,12 @@ args = args.filter(function (arg) { } break; default: - require('../lib/less/lessc_helper').printUsage(); - continueProcessing = false; - currentErrorcode = 1; + if (!pluginManager.interpretCommandLineArgument(arg, match[2])) { + // TODO more of an explanation + require('../lib/less/lessc_helper').printUsage(); + continueProcessing = false; + currentErrorcode = 1; + } break; } }); @@ -368,6 +373,7 @@ var parseLessFile = function (e, data) { options.sourceMapFilename = options.sourceMap; options.sourceMap = Boolean(options.sourceMap); options.sourceMapRootpath = options.sourceMapRootpath || ""; + options.plugins = pluginManager; less.render(data, options) .then(function(css) { diff --git a/lib/less-node/index.js b/lib/less-node/index.js index c4124f90a..57174b49a 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -1,17 +1,19 @@ var environment = require("./environment"), createFromEnvironment = require("../less"), - less = createFromEnvironment(environment); + less = createFromEnvironment(environment), + lesscHelper = require('./lessc-helper'); // allow people to create less with their own environment less.createFromEnvironment = createFromEnvironment; - +less.lesscHelper = lesscHelper; +less.PluginManager = require("./node-plugin-manager"); less.formatError = function(ctx, options) { options = options || {}; var message = ""; var extract = ctx.extract; var error = []; - var stylize = options.color ? require('./lessc-helper').stylize : function (str) { return str; }; + var stylize = options.color ? lesscHelper.stylize : function (str) { return str; }; // only output a stack if it isn't a less error if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } diff --git a/lib/less-node/node-plugin-manager.js b/lib/less-node/node-plugin-manager.js new file mode 100644 index 000000000..547d0c792 --- /dev/null +++ b/lib/less-node/node-plugin-manager.js @@ -0,0 +1,29 @@ +var PluginManager = require("../less/plugin-manager"); +/** + * Node Plugin Manager + */ +var NodePluginManager = function(less) { + PluginManager.call(this, less); +}; +NodePluginManager.prototype = new PluginManager(); +NodePluginManager.prototype.interpretCommandLineArgument = function(name, argument) { + var plugin = this.tryRequirePlugin(name); + if (plugin) { + this.addPlugin(plugin); + return true; + } + return false; +}; +NodePluginManager.prototype.tryRequirePlugin = function(name) { + try { + return require("less-plugin-"+name); + } + catch(e) { + } + try { + return require("../../../less-plugin-"+name); + } + catch(e) { + } +}; +module.exports = NodePluginManager; diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index 03f086cca..3112d80d5 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -6,7 +6,7 @@ module.exports = function(environment) { functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { if (!environment.supportsDataURI(this.env)) { - return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); + return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env); } var mimetype = mimetypeNode.value; @@ -60,7 +60,7 @@ module.exports = function(environment) { console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); } - return new URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env); + return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env); } } @@ -68,6 +68,6 @@ module.exports = function(environment) { : encodeURIComponent(buf); var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; - return new(URL)(new(Anonymous)(uri)); + return new URL(new Anonymous(uri), this.index, this.currentFileInfo); }); }; diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js index 056b817d4..1e4f7965b 100644 --- a/lib/less/functions/function-caller.js +++ b/lib/less/functions/function-caller.js @@ -1,8 +1,9 @@ var functionRegistry = require("./function-registry"); -var functionCaller = function(name, env, currentFileInfo) { +var functionCaller = function(name, env, index, currentFileInfo) { this.name = name.toLowerCase(); this.function = functionRegistry.get(this.name); + this.index = index; this.env = env; this.currentFileInfo = currentFileInfo; }; diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js index 27294e0f2..5bd8fc68b 100644 --- a/lib/less/functions/svg.js +++ b/lib/less/functions/svg.js @@ -78,6 +78,6 @@ module.exports = function(environment) { } returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; - return new(URL)(new(Anonymous)(returner)); + return new URL(new Anonymous(returner), this.index, this.currentFileInfo); }); }; diff --git a/lib/less/index.js b/lib/less/index.js index bd2ea8144..da4731f9e 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -3,12 +3,19 @@ module.exports = function(environment) { version: [2, 0, 0], data: require('./data'), tree: require('./tree'), - visitor: require('./visitor'), + visitors: require('./visitors'), Parser: require('./parser/parser'), functions: require('./functions')(environment), contexts: require("./contexts"), environment: environment, - render: require("./render")(environment) + render: require("./render")(environment), + // ParseTree: require('./parse-tree'), // TODO - move environment to constructor? make available to consumers + //SourceMapOutput: require('./source-map-output.js'), // TODO - move environment to constructor? make available to consumers + getImportManager: require('./imports'), // TODO: change to class? add static ways of replacing file-manager? + LessError: require('./less-error'), + transformTree: require('./transform-tree'), + utils: require('./utils'), + PluginManager: require('./plugin-manager') }; return less; diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 3246e5f3a..d62322385 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -1,6 +1,6 @@ var LessError = require('../less-error'), tree = require("../tree"), - visitor = require("../visitor"), + visitors = require("../visitors"), getParserInput = require("./parser-input"), utils = require("../utils"); @@ -170,7 +170,7 @@ var Parser = function Parser(env, imports) { }; if (env.processImports !== false) { - new visitor.ImportVisitor(imports, finish) + new visitors.ImportVisitor(imports, finish) .run(root); } else { return finish(); @@ -381,7 +381,7 @@ var Parser = function Parser(env, imports) { // to be enclosed within a string, so it can't be parsed as an Expression. // url: function () { - var value; + var value, index = parserInput.i; if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) { return; @@ -396,8 +396,8 @@ var Parser = function Parser(env, imports) { expectChar(')'); - return new(tree.URL)((value.value != null || value instanceof tree.Variable) - ? value : new(tree.Anonymous)(value), env.currentFileInfo); + return new(tree.URL)((value.value != null || value instanceof tree.Variable) ? + value : new(tree.Anonymous)(value), index, env.currentFileInfo); }, // diff --git a/lib/less/plugin-manager.js b/lib/less/plugin-manager.js new file mode 100644 index 000000000..c4be752f0 --- /dev/null +++ b/lib/less/plugin-manager.js @@ -0,0 +1,17 @@ +/** + * Plugin Manager + */ +var PluginManager = function(less) { + this.less = less; + this.visitors = []; +}; +PluginManager.prototype.addPlugin = function(plugin) { + plugin.install(this.less, this); +}; +PluginManager.prototype.addVisitor = function(visitor) { + this.visitors.push(visitor); +}; +PluginManager.prototype.getVisitors = function() { + return this.visitors; +}; +module.exports = PluginManager; diff --git a/lib/less/transform-tree.js b/lib/less/transform-tree.js index cd7453905..7c88bb019 100644 --- a/lib/less/transform-tree.js +++ b/lib/less/transform-tree.js @@ -1,5 +1,5 @@ var contexts = require("./contexts"), - visitor = require("./visitor"), + visitor = require("./visitors"), tree = require("./tree"); module.exports = function(root, options) { @@ -44,14 +44,16 @@ module.exports = function(root, options) { ], i; if (options.plugins) { - for(i =0; i < options.plugins.length; i++) { - if (options.plugins[i].isPreEvalVisitor) { - preEvalVisitors.push(options.plugins[i]); + var pluginVisitors = options.plugins.getVisitors(); + for(i =0; i < pluginVisitors.length; i++) { + var pluginVisitor = pluginVisitors[i]; + if (pluginVisitor.isPreEvalVisitor) { + preEvalVisitors.push(pluginVisitor); } else { - if (options.plugins[i].isPreVisitor) { - visitors.splice(0, 0, options.plugins[i]); + if (pluginVisitor.isPreVisitor) { + visitors.splice(0, 0, pluginVisitor); } else { - visitors.push(options.plugins[i]); + visitors.push(pluginVisitor); } } } diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index 38bcbc0d0..ef7199b2e 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -31,7 +31,7 @@ Call.prototype.accept = function (visitor) { // Call.prototype.eval = function (env) { var args = this.args.map(function (a) { return a.eval(env); }), - result, funcCaller = new FunctionCaller(this.name, env, this.currentFileInfo); + result, funcCaller = new FunctionCaller(this.name, env, this.index, this.currentFileInfo); if (funcCaller.isValid()) { // 1. try { diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 6dd2fa6f9..b528fff71 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -1,8 +1,9 @@ var Node = require("./node"); -var URL = function (val, currentFileInfo, isEvald) { +var URL = function (val, index, currentFileInfo, isEvald) { this.value = val; this.currentFileInfo = currentFileInfo; + this.index = index; this.isEvald = isEvald; }; URL.prototype = new Node(); @@ -45,6 +46,6 @@ URL.prototype.eval = function (ctx) { } } - return new(URL)(val, this.currentFileInfo, true); + return new URL(val, this.index, this.currentFileInfo, true); }; module.exports = URL; diff --git a/lib/less/visitor/index.js b/lib/less/visitor/index.js deleted file mode 100644 index 8a09617f0..000000000 --- a/lib/less/visitor/index.js +++ /dev/null @@ -1,9 +0,0 @@ -var visitors = { - Visitor: require("./visitor"), - ImportVisitor: require('./import-visitor.js'), - ExtendVisitor: require('./extend-visitor.js'), - JoinSelectorVisitor: require('./join-selector-visitor.js'), - ToCSSVisitor: require('./to-css-visitor.js') -}; - -module.exports = visitors; diff --git a/lib/less/visitor/extend-visitor.js b/lib/less/visitors/extend-visitor.js similarity index 99% rename from lib/less/visitor/extend-visitor.js rename to lib/less/visitors/extend-visitor.js index 9616cd6d8..6d17db25d 100644 --- a/lib/less/visitor/extend-visitor.js +++ b/lib/less/visitors/extend-visitor.js @@ -1,5 +1,5 @@ -var tree = require("../tree/index.js"), - Visitor = require("./visitor.js"); +var tree = require("../tree"), + Visitor = require("./visitor"); /*jshint loopfunc:true */ diff --git a/lib/less/visitor/import-visitor.js b/lib/less/visitors/import-visitor.js similarity index 98% rename from lib/less/visitor/import-visitor.js rename to lib/less/visitors/import-visitor.js index f7068df44..015f3bf53 100644 --- a/lib/less/visitor/import-visitor.js +++ b/lib/less/visitors/import-visitor.js @@ -1,5 +1,5 @@ -var contexts = require("../contexts.js"), - Visitor = require("./visitor.js"); +var contexts = require("../contexts"), + Visitor = require("./visitor"); var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { this._visitor = new Visitor(this); diff --git a/lib/less/visitors/index.js b/lib/less/visitors/index.js new file mode 100644 index 000000000..fc8e683f4 --- /dev/null +++ b/lib/less/visitors/index.js @@ -0,0 +1,9 @@ +var visitors = { + Visitor: require("./visitor"), + ImportVisitor: require('./import-visitor'), + ExtendVisitor: require('./extend-visitor'), + JoinSelectorVisitor: require('./join-selector-visitor'), + ToCSSVisitor: require('./to-css-visitor') +}; + +module.exports = visitors; diff --git a/lib/less/visitor/join-selector-visitor.js b/lib/less/visitors/join-selector-visitor.js similarity index 97% rename from lib/less/visitor/join-selector-visitor.js rename to lib/less/visitors/join-selector-visitor.js index 05f8732bf..b144696ce 100644 --- a/lib/less/visitor/join-selector-visitor.js +++ b/lib/less/visitors/join-selector-visitor.js @@ -1,4 +1,4 @@ -var Visitor = require("./visitor.js"); +var Visitor = require("./visitor"); var JoinSelectorVisitor = function() { this.contexts = [[]]; diff --git a/lib/less/visitor/to-css-visitor.js b/lib/less/visitors/to-css-visitor.js similarity index 99% rename from lib/less/visitor/to-css-visitor.js rename to lib/less/visitors/to-css-visitor.js index 1a8f5e248..e9e19d127 100644 --- a/lib/less/visitor/to-css-visitor.js +++ b/lib/less/visitors/to-css-visitor.js @@ -1,5 +1,5 @@ -var tree = require("../tree/index.js"), - Visitor = require("./visitor.js"); +var tree = require("../tree"), + Visitor = require("./visitor"); var ToCSSVisitor = function(env) { this._visitor = new Visitor(this); diff --git a/lib/less/visitor/visitor.js b/lib/less/visitors/visitor.js similarity index 98% rename from lib/less/visitor/visitor.js rename to lib/less/visitors/visitor.js index cd0b32868..5c0b5a510 100644 --- a/lib/less/visitor/visitor.js +++ b/lib/less/visitors/visitor.js @@ -1,4 +1,4 @@ -var tree = require("../tree/index.js"); +var tree = require("../tree"); var _visitArgs = { visitDeeper: true }, _hasIndexed = false; From 4927bf1665f2ae71cb06e11d5995e1e71befef28 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Fri, 12 Sep 2014 14:54:30 +0100 Subject: [PATCH 086/161] Add first draft post-processors, fix render function when using callback. remove regex compression (not compatible with sourcemaps) --- lib/less/parse-tree.js | 12 ++++++++---- lib/less/plugin-manager.js | 22 ++++++++++++++++++++++ lib/less/render.js | 11 ++++++----- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index 1ae303ff1..b7531b815 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -45,11 +45,15 @@ ParseTree.prototype.toCSS = function(options) { throw new LessError(e, this.imports); } - if (options.compress) { - return css.replace(/(^(\s)+)|((\s)+$)/g, ""); - } else { - return css; + if (options.plugins) { + var postProcessors = options.plugins.getPostProcessors(); + for(var i = 0; i < postProcessors.length; i++) { + // TODO - pass source maps + // TODO - async + css = postProcessors.process(css); + } } + return css; }; return ParseTree; }; diff --git a/lib/less/plugin-manager.js b/lib/less/plugin-manager.js index c4be752f0..38bdb965c 100644 --- a/lib/less/plugin-manager.js +++ b/lib/less/plugin-manager.js @@ -4,6 +4,7 @@ var PluginManager = function(less) { this.less = less; this.visitors = []; + this.postProcessors = []; }; PluginManager.prototype.addPlugin = function(plugin) { plugin.install(this.less, this); @@ -11,6 +12,27 @@ PluginManager.prototype.addPlugin = function(plugin) { PluginManager.prototype.addVisitor = function(visitor) { this.visitors.push(visitor); }; +/** + * Adds a post processor object + * @param {object} postProcessor + * @param {number} priority - guidelines 1 = before compression, 1000 = compression, 2000 = after compression + */ +PluginManager.prototype.addPostProcessor = function(postProcessor, priority) { + var indexToInsertAt; + for(indexToInsertAt = 0; indexToInsertAt < this.postProcessors.length; indexToInsertAt++) { + if (this.postProcessors[indexToInsertAt].priority >= priority) { + break; + } + } + this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority}); +}; +PluginManager.prototype.getPostProcessors = function() { + var postProcessors = []; + for(var i = 0; i < this.postProcessors.length; i++) { + postProcessors.push(this.postProcessors[i].postProcessor); + } + return postProcessors; +}; PluginManager.prototype.getVisitors = function() { return this.visitors; }; diff --git a/lib/less/render.js b/lib/less/render.js index 16912075d..dde6eb842 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -3,9 +3,9 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : P getImportManager = require("./imports"), Parser = require('./parser/parser'); -var render = function(environment) { +module.exports = function(environment) { var ParseTree = require("./parse-tree")(environment); - return function (input, options, callback) { + var render = function (input, options, callback) { options = options || {}; if (typeof(options) === 'function') { @@ -14,7 +14,7 @@ var render = function(environment) { } if (callback) { - render(input.options) + render(input, options) .then(function(css) { callback(null, css); }, @@ -32,12 +32,13 @@ var render = function(environment) { if (e) { return reject(e); } try { var parseTree = new ParseTree(root, imports); - resolve(parseTree.toCSS(options)); + var css = parseTree.toCSS(options); + resolve(css); } catch (err) { reject( err); } }, options); }); } }; + return render; }; -module.exports = render; From 1ecce8b3cac4b00d7491c43764b313ad3406051a Mon Sep 17 00:00:00 2001 From: Max Mikhailov Date: Wed, 17 Sep 2014 11:45:57 +0400 Subject: [PATCH 087/161] tree/directive.js: fix incorrect accept() code Fixes #2191. --- lib/less/tree/directive.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index b39df719b..c69eee269 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -18,10 +18,10 @@ Directive.prototype.type = "Directive"; Directive.prototype.accept = function (visitor) { var value = this.value, rules = this.rules; if (rules) { - rules = visitor.visit(rules); + this.rules = visitor.visit(rules); } if (value) { - value = visitor.visit(value); + this.value = visitor.visit(value); } }; Directive.prototype.isRulesetLike = function() { From 6ea7a1995ec918b965ca92f3dfd6201373c20892 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 17 Sep 2014 15:09:04 +0100 Subject: [PATCH 088/161] move out clean css --- bin/lessc | 67 ++-------------------------- lib/less-node/node-plugin-manager.js | 4 +- lib/less/parse-tree.js | 2 +- lib/less/plugin-manager.js | 4 +- package.json | 1 - 5 files changed, 8 insertions(+), 70 deletions(-) diff --git a/bin/lessc b/bin/lessc index 50fc18f2d..021b4cef5 100755 --- a/bin/lessc +++ b/bin/lessc @@ -12,7 +12,6 @@ var args = process.argv.slice(1); var options = { depends: false, compress: false, - cleancss: false, max_line_len: -1, silent: false, verbose: false, @@ -30,7 +29,6 @@ var options = { modifyVariables: '', urlArgs: '' }; -var cleancssOptions = {}; var continueProcessing = true, currentErrorcode; @@ -114,12 +112,6 @@ args = args.filter(function (arg) { case 'depends': options.depends = true; break; - case 'yui-compress': - warningMessages += "yui-compress option has been removed. ignoring."; - break; - case 'clean-css': - options.cleancss = true; - break; case 'max-line-len': if (checkArgFunc(arg, match[2])) { options.maxLineLen = parseInt(match[2], 10); @@ -213,40 +205,6 @@ args = args.filter(function (arg) { options.modifyVariables += parseVariableOption(match[2]); } break; - case "clean-option": - var cleanOptionArgs = match[2].split(":"); - switch(cleanOptionArgs[0]) { - case "--keep-line-breaks": - case "-b": - cleancssOptions.keepBreaks = true; - break; - case "--s0": - cleancssOptions.keepSpecialComments = 0; - break; - case "--s1": - cleancssOptions.keepSpecialComments = 1; - break; - case "--skip-advanced": - cleancssOptions.noAdvanced = true; - break; - case "--advanced": - cleancssOptions.noAdvanced = false; - break; - case "--compatibility": - cleancssOptions.compatibility = cleanOptionArgs[1]; - break; - case "--rounding-precision": - cleancssOptions.roundingPrecision = Number(cleanOptionArgs[1]); - break; - default: - console.log("unrecognised clean-css option '" + cleanOptionArgs[0] + "'"); - console.log("we support only arguments that make sense for less, '--keep-line-breaks', '-b'"); - console.log("'--s0', '--s1', '--advanced', '--skip-advanced', '--compatibility', '--rounding-precision'"); - continueProcessing = false; - currentErrorcode = 1; - break; - } - break; case 'url-args': if (checkArgFunc(arg, match[2])) { options.urlArgs = match[2]; @@ -254,8 +212,9 @@ args = args.filter(function (arg) { break; default: if (!pluginManager.interpretCommandLineArgument(arg, match[2])) { - // TODO more of an explanation - require('../lib/less/lessc_helper').printUsage(); + console.log("Unable to interpret argument " + arg + " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under at the same level as less"); + console.log(); + require('../lib/less-node/lessc-helper').printUsage(); continueProcessing = false; currentErrorcode = 1; } @@ -292,11 +251,6 @@ if (options.sourceMap === true) { options.sourceMap = path.basename(options.sourceMapFullFilename); } -if (options.cleancss && options.sourceMap) { - console.log("the cleancss option is not compatible with sourcemap support at the moment. See Issue #1656"); - return; -} - if (! input) { console.log("lessc: no input files"); console.log(""); @@ -378,21 +332,6 @@ var parseLessFile = function (e, data) { less.render(data, options) .then(function(css) { if(!options.lint) { - if (options.cleancss) { - var CleanCSS = require('clean-css'); - var cleancssOptions = options.cleancssOptions || {}; - - if (cleancssOptions.keepSpecialComments === undefined) { - cleancssOptions.keepSpecialComments = "*"; - } - cleancssOptions.processImport = false; - cleancssOptions.noRebase = true; - if (cleancssOptions.noAdvanced === undefined) { - cleancssOptions.noAdvanced = true; - } - - css = new CleanCSS(cleancssOptions).minify(css); - } if (output) { ensureDirectory(output); fs.writeFileSync(output, css, 'utf8'); diff --git a/lib/less-node/node-plugin-manager.js b/lib/less-node/node-plugin-manager.js index 547d0c792..465570a88 100644 --- a/lib/less-node/node-plugin-manager.js +++ b/lib/less-node/node-plugin-manager.js @@ -9,7 +9,7 @@ NodePluginManager.prototype = new PluginManager(); NodePluginManager.prototype.interpretCommandLineArgument = function(name, argument) { var plugin = this.tryRequirePlugin(name); if (plugin) { - this.addPlugin(plugin); + this.addPlugin(plugin, argument); return true; } return false; @@ -21,7 +21,7 @@ NodePluginManager.prototype.tryRequirePlugin = function(name) { catch(e) { } try { - return require("../../../less-plugin-"+name); + return require("../../../less-plugin-" + name); } catch(e) { } diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index b7531b815..7788ce019 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -50,7 +50,7 @@ ParseTree.prototype.toCSS = function(options) { for(var i = 0; i < postProcessors.length; i++) { // TODO - pass source maps // TODO - async - css = postProcessors.process(css); + css = postProcessors[i].process(css); } } return css; diff --git a/lib/less/plugin-manager.js b/lib/less/plugin-manager.js index 38bdb965c..a093aa4f2 100644 --- a/lib/less/plugin-manager.js +++ b/lib/less/plugin-manager.js @@ -6,8 +6,8 @@ var PluginManager = function(less) { this.visitors = []; this.postProcessors = []; }; -PluginManager.prototype.addPlugin = function(plugin) { - plugin.install(this.less, this); +PluginManager.prototype.addPlugin = function(plugin, options) { + plugin.install(this.less, this, options); }; PluginManager.prototype.addVisitor = function(visitor) { this.visitors.push(visitor); diff --git a/package.json b/package.json index ff44cfb7b..c3022d248 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "mime": "~1.2.11", "request": "~2.40.0", "mkdirp": "~0.5.0", - "clean-css": "2.2.x", "source-map": "0.1.x", "promise": "~5.0.0" }, From 771ab4e430485d18a707ace7482bfafb64df71ec Mon Sep 17 00:00:00 2001 From: Luke Page Date: Wed, 17 Sep 2014 21:14:40 +0100 Subject: [PATCH 089/161] sort out usage and allow a plugin argument --- bin/lessc | 28 ++++++++++++++++++++++------ lib/less-node/lessc-helper.js | 11 ++++++----- lib/less-node/node-plugin-manager.js | 22 ++++++++++++++++------ lib/less/plugin-manager.js | 2 ++ 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/bin/lessc b/bin/lessc index 021b4cef5..129b74a8f 100755 --- a/bin/lessc +++ b/bin/lessc @@ -63,6 +63,12 @@ var parseVariableOption = function(option) { var warningMessages = ""; var sourceMapFileInline = false; +function printUsage() { + require('../lib/less-node/lessc-helper').printUsage(); + pluginManager.printUsage(); + continueProcessing = false; +} + args = args.filter(function (arg) { var match; @@ -98,8 +104,7 @@ args = args.filter(function (arg) { break; case 'h': case 'help': - require('../lib/less-node/lessc-helper').printUsage(); - continueProcessing = false; + printUsage(); break; case 'x': case 'compress': @@ -210,12 +215,23 @@ args = args.filter(function (arg) { options.urlArgs = match[2]; } break; + case 'plugin': + var splitupArg = match[2].match(/^([^=]+)(=(.*))?/), + name = splitupArg[1], + pluginOptions = splitupArg[3]; + + if (!pluginManager.tryLoadPlugin(name, pluginOptions)) { + console.log("Unable to load plugin " + name + " please make sure that it is installed under at the same level as less"); + console.log(); + printUsage(); + currentErrorcode = 1; + } + break; default: - if (!pluginManager.interpretCommandLineArgument(arg, match[2])) { + if (!pluginManager.tryLoadPlugin("less-plugin-" + arg, match[2])) { console.log("Unable to interpret argument " + arg + " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under at the same level as less"); console.log(); - require('../lib/less-node/lessc-helper').printUsage(); - continueProcessing = false; + printUsage(); currentErrorcode = 1; } break; @@ -254,7 +270,7 @@ if (options.sourceMap === true) { if (! input) { console.log("lessc: no input files"); console.log(""); - require('../lib/less/lessc_helper').printUsage(); + printUsage(); currentErrorcode = 1; return; } diff --git a/lib/less-node/lessc-helper.js b/lib/less-node/lessc-helper.js index 811895dca..ac5d3deb3 100644 --- a/lib/less-node/lessc-helper.js +++ b/lib/less-node/lessc-helper.js @@ -38,11 +38,6 @@ var lessc_helper = { console.log(" --insecure Allow imports from insecure https hosts."); console.log(" -v, --version Print version number and exit."); console.log(" -x, --compress Compress output by removing some whitespaces."); - console.log(" --clean-css Compress output using clean-css"); - console.log(" --clean-option=opt:val Pass an option to clean css, using CLI arguments from "); - console.log(" https://github.com/GoalSmashers/clean-css e.g."); - console.log(" --clean-option=--selectors-merge-mode:ie8"); - console.log(" and to switch on advanced use --clean-option=--advanced"); console.log(" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map)"); console.log(" --source-map-rootpath=X adds this path onto the sourcemap filename and less file paths"); console.log(" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory."); @@ -60,6 +55,12 @@ var lessc_helper = { console.log(" --global-var='VAR=VALUE' Defines a variable that can be referenced by the file."); console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file."); console.log(" --url-args='QUERYSTRING' Adds params into url tokens (e.g. 42, cb=42 or 'a=1&b=2')"); + console.log(" --plugin=PLUGIN=OPTIONS Loads a plugin. You can also omit the --plugin= if the plugin begins"); + console.log(" less-plugin. E.g. the clean css plugin is called less-plugin-clean-css"); + console.log(" once installed (npm install less-plugin-clean-css), use either with"); + console.log(" --plugin=less-plugin-clean-css or just --clean-css"); + console.log(" specify options afterwards e.g. --plugin=less-plugin-clean-css=\"advanced\""); + console.log(" or --clean-css=\"advanced\""); console.log(""); console.log("-------------------------- Deprecated ----------------"); console.log(" --line-numbers=TYPE Outputs filename and line numbers."); diff --git a/lib/less-node/node-plugin-manager.js b/lib/less-node/node-plugin-manager.js index 465570a88..2e06de7fc 100644 --- a/lib/less-node/node-plugin-manager.js +++ b/lib/less-node/node-plugin-manager.js @@ -6,7 +6,7 @@ var NodePluginManager = function(less) { PluginManager.call(this, less); }; NodePluginManager.prototype = new PluginManager(); -NodePluginManager.prototype.interpretCommandLineArgument = function(name, argument) { +NodePluginManager.prototype.tryLoadPlugin = function(name, argument) { var plugin = this.tryRequirePlugin(name); if (plugin) { this.addPlugin(plugin, argument); @@ -15,15 +15,25 @@ NodePluginManager.prototype.interpretCommandLineArgument = function(name, argume return false; }; NodePluginManager.prototype.tryRequirePlugin = function(name) { - try { - return require("less-plugin-"+name); - } - catch(e) { + if (name[0] !== '.') { + try { + return require(name); + } + catch(e) { + } } try { - return require("../../../less-plugin-" + name); + return require("../../../" + name); } catch(e) { } }; +NodePluginManager.prototype.printUsage = function() { + for(var i = 0; i < this.installedPlugins.length; i++) { + var plugin = this.installedPlugins[i]; + if (plugin.printUsage) { + plugin.printUsage(); + } + } +}; module.exports = NodePluginManager; diff --git a/lib/less/plugin-manager.js b/lib/less/plugin-manager.js index a093aa4f2..721faf4d5 100644 --- a/lib/less/plugin-manager.js +++ b/lib/less/plugin-manager.js @@ -5,8 +5,10 @@ var PluginManager = function(less) { this.less = less; this.visitors = []; this.postProcessors = []; + this.installedPlugins = []; }; PluginManager.prototype.addPlugin = function(plugin, options) { + this.installedPlugins.push(plugin); plugin.install(this.less, this, options); }; PluginManager.prototype.addVisitor = function(visitor) { From 86f78fa19704b454704f983a15997fc9453c0432 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 18 Sep 2014 12:53:05 +0100 Subject: [PATCH 090/161] support min version --- bin/lessc | 2 +- lib/less-node/index.js | 2 ++ lib/less-node/node-plugin-manager.js | 23 +++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/bin/lessc b/bin/lessc index 129b74a8f..f0a27ddf5 100755 --- a/bin/lessc +++ b/bin/lessc @@ -64,7 +64,7 @@ var warningMessages = ""; var sourceMapFileInline = false; function printUsage() { - require('../lib/less-node/lessc-helper').printUsage(); + less.lesscHelper.printUsage(); pluginManager.printUsage(); continueProcessing = false; } diff --git a/lib/less-node/index.js b/lib/less-node/index.js index 57174b49a..63f2ded5e 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -7,6 +7,8 @@ var environment = require("./environment"), less.createFromEnvironment = createFromEnvironment; less.lesscHelper = lesscHelper; less.PluginManager = require("./node-plugin-manager"); +less.fs = require("./fs"); +less.environment = environment; less.formatError = function(ctx, options) { options = options || {}; diff --git a/lib/less-node/node-plugin-manager.js b/lib/less-node/node-plugin-manager.js index 2e06de7fc..a013fcca0 100644 --- a/lib/less-node/node-plugin-manager.js +++ b/lib/less-node/node-plugin-manager.js @@ -9,11 +9,34 @@ NodePluginManager.prototype = new PluginManager(); NodePluginManager.prototype.tryLoadPlugin = function(name, argument) { var plugin = this.tryRequirePlugin(name); if (plugin) { + if (plugin.minVersion) { + if (this.compareVersion(plugin.minVersion, this.less.version) < 0) { + console.log("plugin " + name + " requires version " + this.versionToString(plugin.minVersion)); + return false; + } + } else { + console.log("plugin has no min version"); + } this.addPlugin(plugin, argument); return true; } return false; }; +NodePluginManager.prototype.compareVersion = function(aVersion, bVersion) { + for(var i = 0; i < aVersion.length; i++) { + if (aVersion[i] !== bVersion[i]) { + return aVersion[i] > bVersion[i] ? -1 : 1; + } + } + return 0; +}; +NodePluginManager.prototype.versionToString = function(version) { + var versionString = ""; + for(var i = 0; i < version.length; i++) { + versionString += (versionString ? "." : "") + version[i]; + } + return versionString; +}; NodePluginManager.prototype.tryRequirePlugin = function(name) { if (name[0] !== '.') { try { From bc335d142f3f9b7d35693231a9a9e7a8d3f1a676 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 18 Sep 2014 14:03:25 +0100 Subject: [PATCH 091/161] move rhino to its own folder --- lib/{less/rhino.js => less-rhino/index.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/{less/rhino.js => less-rhino/index.js} (100%) diff --git a/lib/less/rhino.js b/lib/less-rhino/index.js similarity index 100% rename from lib/less/rhino.js rename to lib/less-rhino/index.js From b721c4f44fa7aef7af91984156c652ac9e705e54 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 18 Sep 2014 22:23:15 +0100 Subject: [PATCH 092/161] use a sourcemap builder for sourcemap output, seperated from the other less options --- bin/lessc | 14 ++++++++----- lib/less/index.js | 8 ++++++-- lib/less/parse-tree.js | 36 ++++++++++------------------------ lib/less/render.js | 4 ++-- lib/less/source-map-builder.js | 27 +++++++++++++++++++++++++ test/less-test.js | 2 ++ 6 files changed, 56 insertions(+), 35 deletions(-) create mode 100644 lib/less/source-map-builder.js diff --git a/bin/lessc b/bin/lessc index f0a27ddf5..f1931eace 100755 --- a/bin/lessc +++ b/bin/lessc @@ -299,7 +299,7 @@ if (options.depends) { if (!sourceMapFileInline) { var writeSourceMap = function(output) { - var filename = options.sourceMapFullFilename || options.sourceMap; + var filename = options.sourceMapFullFilename || options.sourceMapFilename; ensureDirectory(filename); fs.writeFileSync(filename, output, 'utf8'); }; @@ -336,13 +336,17 @@ var parseLessFile = function (e, data) { } if (options.lint) { - options.writeSourceMap = function() {}; - } else { - options.writeSourceMap = writeSourceMap; + options.sourceMap = false; } + options.writeSourceMap = writeSourceMap; options.sourceMapFilename = options.sourceMap; - options.sourceMap = Boolean(options.sourceMap); options.sourceMapRootpath = options.sourceMapRootpath || ""; + + if (options.sourceMap) { + // todo seperate options - no need to send them to less any more? + options.sourceMap = new less.SourceMapBuilder(options); + } + options.plugins = pluginManager; less.render(data, options) diff --git a/lib/less/index.js b/lib/less/index.js index da4731f9e..bd259b2fc 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,4 +1,7 @@ module.exports = function(environment) { + var SourceMapOutput = require('./source-map-output.js')(environment), + SourceMapBuilder = require('./source-map-builder.js')(SourceMapOutput); + var less = { version: [2, 0, 0], data: require('./data'), @@ -9,8 +12,9 @@ module.exports = function(environment) { contexts: require("./contexts"), environment: environment, render: require("./render")(environment), - // ParseTree: require('./parse-tree'), // TODO - move environment to constructor? make available to consumers - //SourceMapOutput: require('./source-map-output.js'), // TODO - move environment to constructor? make available to consumers + ParseTree: require('./parse-tree'), + SourceMapOutput: SourceMapOutput, + SourceMapBuilder: SourceMapBuilder, getImportManager: require('./imports'), // TODO: change to class? add static ways of replacing file-manager? LessError: require('./less-error'), transformTree: require('./transform-tree'), diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index 7788ce019..e1aeeaee2 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -1,10 +1,6 @@ var LessError = require('./less-error'), transformTree = require("./transform-tree"); -module.exports = function(environment) -{ -var SourceMapOutput = require("./source-map-output")(environment); - var ParseTree = function(root, imports) { this.root = root; this.imports = imports; @@ -19,28 +15,17 @@ ParseTree.prototype.toCSS = function(options) { } var css; try { - if (options.sourceMap) { - evaldRoot = new SourceMapOutput( - { - contentsIgnoredCharsMap: this.imports.contentsIgnoredChars, - writeSourceMap: options.writeSourceMap, - rootNode: evaldRoot, - contentsMap: this.imports.contents, - sourceMapFilename: options.sourceMapFilename, - sourceMapURL: options.sourceMapURL, - outputFilename: options.sourceMapOutputFilename, - sourceMapBasepath: options.sourceMapBasepath, - sourceMapRootpath: options.sourceMapRootpath, - outputSourceFiles: options.outputSourceFiles, - sourceMapGenerator: options.sourceMapGenerator - }); - } - - css = evaldRoot.toCSS({ + var toCSSOptions = { compress: Boolean(options.compress), dumpLineNumbers: options.dumpLineNumbers, strictUnits: Boolean(options.strictUnits), - numPrecision: 8}); + numPrecision: 8}; + + if (options.sourceMap) { + css = options.sourceMap.toCSS(evaldRoot, toCSSOptions, this.imports); + } else { + css = evaldRoot.toCSS(toCSSOptions); + } } catch (e) { throw new LessError(e, this.imports); } @@ -50,10 +35,9 @@ ParseTree.prototype.toCSS = function(options) { for(var i = 0; i < postProcessors.length; i++) { // TODO - pass source maps // TODO - async - css = postProcessors[i].process(css); + css = postProcessors[i].process(css, options); } } return css; }; -return ParseTree; -}; +module.exports = ParseTree; diff --git a/lib/less/render.js b/lib/less/render.js index dde6eb842..3048d9298 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,10 +1,10 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, contexts = require("./contexts"), getImportManager = require("./imports"), - Parser = require('./parser/parser'); + Parser = require('./parser/parser'), + ParseTree = require("./parse-tree"); module.exports = function(environment) { - var ParseTree = require("./parse-tree")(environment); var render = function (input, options, callback) { options = options || {}; diff --git a/lib/less/source-map-builder.js b/lib/less/source-map-builder.js new file mode 100644 index 000000000..5cd6f91f4 --- /dev/null +++ b/lib/less/source-map-builder.js @@ -0,0 +1,27 @@ +module.exports = function (SourceMapOutput) { + + var SourceMapBuilder = function (options) { + this.options = options; + }; + + SourceMapBuilder.prototype.toCSS = function(rootNode, options, imports) { + this.sourceMapOutput = new SourceMapOutput( + { + contentsIgnoredCharsMap: imports.contentsIgnoredChars, + writeSourceMap: this.options.writeSourceMap, + rootNode: rootNode, + contentsMap: imports.contents, + sourceMapFilename: this.options.sourceMapFilename, + sourceMapURL: this.options.sourceMapURL, + outputFilename: this.options.sourceMapOutputFilename, + sourceMapBasepath: this.options.sourceMapBasepath, + sourceMapRootpath: this.options.sourceMapRootpath, + outputSourceFiles: this.options.outputSourceFiles, + sourceMapGenerator: this.options.sourceMapGenerator + }); + + return this.sourceMapOutput.toCSS(options); + }; + + return SourceMapBuilder; +}; diff --git a/test/less-test.js b/test/less-test.js index 2da185c06..fbb1d99c7 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -117,6 +117,8 @@ module.exports = function() { options.sourceMapOutputFilename = name + ".css"; options.sourceMapBasepath = path.join(process.cwd(), "test/less"); options.sourceMapRootpath = "testweb/"; + // TODO seperate options? + options.sourceMap = new less.SourceMapBuilder(options); } options.getVars = function(file) { From d9a6b462348c8fbb2fbed52650b91d6b3ada1685 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Fri, 19 Sep 2014 14:45:41 +0100 Subject: [PATCH 093/161] initial work to allow post process plugins to work with sourcemaps --- bin/lessc | 6 +++++- lib/less/parse-tree.js | 2 +- lib/less/source-map-builder.js | 25 +++++++++++++++++++++---- lib/less/source-map-output.js | 13 +++++++------ test/less-test.js | 6 +----- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/bin/lessc b/bin/lessc index f1931eace..e42aac628 100755 --- a/bin/lessc +++ b/bin/lessc @@ -338,9 +338,9 @@ var parseLessFile = function (e, data) { if (options.lint) { options.sourceMap = false; } - options.writeSourceMap = writeSourceMap; options.sourceMapFilename = options.sourceMap; options.sourceMapRootpath = options.sourceMapRootpath || ""; + options.sourceMapFileInline = sourceMapFileInline; if (options.sourceMap) { // todo seperate options - no need to send them to less any more? @@ -361,6 +361,10 @@ var parseLessFile = function (e, data) { } else { process.stdout.write(css); } + if (options.sourceMap && !sourceMapFileInline) { + // TODO change to return object instead of css.. so much simpler and can also return metrics + writeSourceMap(options.sourceMap.getExternalSourceMap()); + } } }, function(err) { diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index e1aeeaee2..baa4d9381 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -35,7 +35,7 @@ ParseTree.prototype.toCSS = function(options) { for(var i = 0; i < postProcessors.length; i++) { // TODO - pass source maps // TODO - async - css = postProcessors[i].process(css, options); + css = postProcessors[i].process(css, options, this.imports); } } return css; diff --git a/lib/less/source-map-builder.js b/lib/less/source-map-builder.js index 5cd6f91f4..68df61881 100644 --- a/lib/less/source-map-builder.js +++ b/lib/less/source-map-builder.js @@ -5,10 +5,9 @@ module.exports = function (SourceMapOutput) { }; SourceMapBuilder.prototype.toCSS = function(rootNode, options, imports) { - this.sourceMapOutput = new SourceMapOutput( + var sourceMapOutput = new SourceMapOutput( { contentsIgnoredCharsMap: imports.contentsIgnoredChars, - writeSourceMap: this.options.writeSourceMap, rootNode: rootNode, contentsMap: imports.contents, sourceMapFilename: this.options.sourceMapFilename, @@ -17,10 +16,28 @@ module.exports = function (SourceMapOutput) { sourceMapBasepath: this.options.sourceMapBasepath, sourceMapRootpath: this.options.sourceMapRootpath, outputSourceFiles: this.options.outputSourceFiles, - sourceMapGenerator: this.options.sourceMapGenerator + sourceMapGenerator: this.options.sourceMapGenerator, + sourceMapFileInline: this.options.sourceMapFileInline }); - return this.sourceMapOutput.toCSS(options); + var css = sourceMapOutput.toCSS(options); + this.sourceMap = sourceMapOutput.sourceMap; + this.sourceMapURL = sourceMapOutput.sourceMapURL; + return css; + }; + + SourceMapBuilder.prototype.getExternalSourceMap = function() { + return this.sourceMap; + }; + SourceMapBuilder.prototype.setExternalSourceMap = function(sourceMap) { + this.sourceMap = sourceMap; + }; + + SourceMapBuilder.prototype.isInline = function() { + return this.options.sourceMapFileInline; + }; + SourceMapBuilder.prototype.getSourceMapURL = function() { + return this.sourceMapURL; }; return SourceMapBuilder; diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index a70889ba2..5f4c9de60 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -3,18 +3,18 @@ module.exports = function (environment) { var SourceMapOutput = function (options) { this._css = []; this._rootNode = options.rootNode; - this._writeSourceMap = options.writeSourceMap; this._contentsMap = options.contentsMap; this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; this._sourceMapFilename = options.sourceMapFilename; this._outputFilename = options.outputFilename; - this._sourceMapURL = options.sourceMapURL; + this.sourceMapURL = options.sourceMapURL; if (options.sourceMapBasepath) { this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); } this._sourceMapRootpath = options.sourceMapRootpath; this._outputSourceFiles = options.outputSourceFiles; this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator(); + this._sourceMapFileInline = options.sourceMapFileInline; if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { this._sourceMapRootpath += '/'; @@ -118,14 +118,15 @@ module.exports = function (environment) { var sourceMapURL, sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); - if (this._sourceMapURL) { - sourceMapURL = this._sourceMapURL; + if (this.sourceMapURL) { + sourceMapURL = this.sourceMapURL; } else if (this._sourceMapFilename) { sourceMapURL = this.normalizeFilename(this._sourceMapFilename); } + this.sourceMapURL = sourceMapURL; - if (this._writeSourceMap) { - this._writeSourceMap(sourceMapContent); + if (!this._sourceMapFileInline) { + this.sourceMap = sourceMapContent; } else { sourceMapURL = "data:application/json;base64," + environment.encodeBase64(sourceMapContent); } diff --git a/test/less-test.js b/test/less-test.js index fbb1d99c7..f00582edc 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -110,10 +110,6 @@ module.exports = function() { totalTests++; if (options.sourceMap) { - var sourceMapOutput; - options.writeSourceMap = function(output) { - sourceMapOutput = output; - }; options.sourceMapOutputFilename = name + ".css"; options.sourceMapBasepath = path.join(process.cwd(), "test/less"); options.sourceMapRootpath = "testweb/"; @@ -128,7 +124,7 @@ module.exports = function() { toCSS(options, path.join('test/less/', foldername + file), function (err, less) { if (verifyFunction) { - return verifyFunction(name, err, less, doReplacements, sourceMapOutput); + return verifyFunction(name, err, less, doReplacements, options.sourceMap.getExternalSourceMap()); } var css_name = name; if(nameModifier) { css_name = nameModifier(name); } From e1726029381d56ad85695d9a3a19da844eab9020 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 20 Sep 2014 00:29:05 +0100 Subject: [PATCH 094/161] change render and tree to return an object containing css and map and in the future possibly other things --- bin/lessc | 9 ++++----- lib/less-browser/index.js | 10 +++++----- lib/less/parse-tree.js | 15 +++++++++------ lib/less/render.js | 4 ++-- test/less-test.js | 12 ++++++------ test/modify-vars.js | 6 +++--- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/bin/lessc b/bin/lessc index e42aac628..3f1b327e5 100755 --- a/bin/lessc +++ b/bin/lessc @@ -350,20 +350,19 @@ var parseLessFile = function (e, data) { options.plugins = pluginManager; less.render(data, options) - .then(function(css) { + .then(function(result) { if(!options.lint) { if (output) { ensureDirectory(output); - fs.writeFileSync(output, css, 'utf8'); + fs.writeFileSync(output, result.css, 'utf8'); if (options.verbose) { console.log('lessc: wrote ' + output); } } else { - process.stdout.write(css); + process.stdout.write(result.css); } if (options.sourceMap && !sourceMapFileInline) { - // TODO change to return object instead of css.. so much simpler and can also return metrics - writeSourceMap(options.sourceMap.getExternalSourceMap()); + writeSourceMap(result.map); } } }, diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index ba43b44e4..a767c60e4 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -350,12 +350,12 @@ function loadStyles(modifyVars) { /*jshint loopfunc:true */ // use closure to store current style less.render(lessText, instanceOptions) - .then(bind(function(style, css) { + .then(bind(function(style, result) { style.type = 'text/css'; if (style.styleSheet) { - style.styleSheet.cssText = css; + style.styleSheet.cssText = result.css; } else { - style.innerHTML = css; + style.innerHTML = result.css; } }, null, style), function(e) { @@ -411,8 +411,8 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { if (data) { instanceOptions.currentFileInfo = newFileInfo; less.render(data, instanceOptions) - .then(function(css) { - callback(null, css, data, sheet, webInfo, path); + .then(function(result) { + callback(null, result.css, data, sheet, webInfo, path); }, function(e) { callback(e, null, null, sheet); diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index baa4d9381..c0ade10e8 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -7,13 +7,13 @@ var ParseTree = function(root, imports) { }; ParseTree.prototype.toCSS = function(options) { - var evaldRoot; + var evaldRoot, result = {}; try { evaldRoot = transformTree(this.root, options); } catch (e) { throw new LessError(e, this.imports); } - var css; + try { var toCSSOptions = { compress: Boolean(options.compress), @@ -22,9 +22,9 @@ ParseTree.prototype.toCSS = function(options) { numPrecision: 8}; if (options.sourceMap) { - css = options.sourceMap.toCSS(evaldRoot, toCSSOptions, this.imports); + result.css = options.sourceMap.toCSS(evaldRoot, toCSSOptions, this.imports); } else { - css = evaldRoot.toCSS(toCSSOptions); + result.css = evaldRoot.toCSS(toCSSOptions); } } catch (e) { throw new LessError(e, this.imports); @@ -35,9 +35,12 @@ ParseTree.prototype.toCSS = function(options) { for(var i = 0; i < postProcessors.length; i++) { // TODO - pass source maps // TODO - async - css = postProcessors[i].process(css, options, this.imports); + result.css = postProcessors[i].process(result.css, options, this.imports); } } - return css; + if (options.sourceMap) { + result.map = options.sourceMap.getExternalSourceMap(); + } + return result; }; module.exports = ParseTree; diff --git a/lib/less/render.js b/lib/less/render.js index 3048d9298..ad98a1997 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -32,8 +32,8 @@ module.exports = function(environment) { if (e) { return reject(e); } try { var parseTree = new ParseTree(root, imports); - var css = parseTree.toCSS(options); - resolve(css); + var result = parseTree.toCSS(options); + resolve(result); } catch (err) { reject( err); } }, options); diff --git a/test/less-test.js b/test/less-test.js index f00582edc..8719ac9c9 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -121,10 +121,10 @@ module.exports = function() { return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars'), 'utf8')); }; - toCSS(options, path.join('test/less/', foldername + file), function (err, less) { + toCSS(options, path.join('test/less/', foldername + file), function (err, result) { if (verifyFunction) { - return verifyFunction(name, err, less, doReplacements, options.sourceMap.getExternalSourceMap()); + return verifyFunction(name, err, result.css, doReplacements, result.map); } var css_name = name; if(nameModifier) { css_name = nameModifier(name); } @@ -132,7 +132,7 @@ module.exports = function() { process.stdout.write("- " + css_name + ": "); css = css && doReplacements(css, 'test/less/' + foldername); - if (less === css) { ok('OK'); } + if (result.css === css) { ok('OK'); } else if (err) { fail("ERROR: " + (err && err.message)); if (isVerbose) { @@ -140,7 +140,7 @@ module.exports = function() { process.stdout.write(err.stack + "\n"); } } else { - difference("FAIL", css, less); + difference("FAIL", css, result.css); } }); }); @@ -216,9 +216,9 @@ module.exports = function() { } less.render(str, options) - .then(function(css) { + .then(function(result) { // TODO integration test that calling toCSS twice results in the same css? - callback(null, css); + callback(null, result); }, function(e) { callback(e); }); diff --git a/test/modify-vars.js b/test/modify-vars.js index c4c6c5b4f..2aa324ff5 100644 --- a/test/modify-vars.js +++ b/test/modify-vars.js @@ -7,11 +7,11 @@ var options = { modifyVars: JSON.parse(fs.readFileSync("./test/less/modifyVars/extended.json", 'utf8')) } -less.render(input, options, function (err, css) { +less.render(input, options, function (err, result) { if (err) console.log(err); - if (css === expectedCss) { + if (result.css === expectedCss) { console.log("PASS") } else { console.log("FAIL") } -}) \ No newline at end of file +}) From 5dbae366422917152faca4f4c2d93d0cebbd7d06 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 20 Sep 2014 00:43:02 +0100 Subject: [PATCH 095/161] Do not require users to construct a source map builder --- bin/lessc | 2 +- lib/less/index.js | 11 +++++------ lib/less/parse-tree.js | 14 ++++++++------ lib/less/render.js | 5 ++--- test/less-test.js | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/bin/lessc b/bin/lessc index 3f1b327e5..18b42f7d5 100755 --- a/bin/lessc +++ b/bin/lessc @@ -344,7 +344,7 @@ var parseLessFile = function (e, data) { if (options.sourceMap) { // todo seperate options - no need to send them to less any more? - options.sourceMap = new less.SourceMapBuilder(options); + options.sourceMap = options; } options.plugins = pluginManager; diff --git a/lib/less/index.js b/lib/less/index.js index bd259b2fc..5b45e6529 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,6 +1,5 @@ module.exports = function(environment) { - var SourceMapOutput = require('./source-map-output.js')(environment), - SourceMapBuilder = require('./source-map-builder.js')(SourceMapOutput); + var SourceMapOutput, SourceMapBuilder, ParseTree; var less = { version: [2, 0, 0], @@ -11,10 +10,10 @@ module.exports = function(environment) { functions: require('./functions')(environment), contexts: require("./contexts"), environment: environment, - render: require("./render")(environment), - ParseTree: require('./parse-tree'), - SourceMapOutput: SourceMapOutput, - SourceMapBuilder: SourceMapBuilder, + SourceMapOutput: (SourceMapOutput = require('./source-map-output.js')(environment)), + SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder.js')(SourceMapOutput)), + ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)), + render: require("./render")(environment, ParseTree), getImportManager: require('./imports'), // TODO: change to class? add static ways of replacing file-manager? LessError: require('./less-error'), transformTree: require('./transform-tree'), diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index c0ade10e8..f04749f97 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -1,13 +1,14 @@ var LessError = require('./less-error'), transformTree = require("./transform-tree"); +module.exports = function(SourceMapBuilder) { var ParseTree = function(root, imports) { this.root = root; this.imports = imports; }; ParseTree.prototype.toCSS = function(options) { - var evaldRoot, result = {}; + var evaldRoot, result = {}, sourceMapBuilder; try { evaldRoot = transformTree(this.root, options); } catch (e) { @@ -22,7 +23,8 @@ ParseTree.prototype.toCSS = function(options) { numPrecision: 8}; if (options.sourceMap) { - result.css = options.sourceMap.toCSS(evaldRoot, toCSSOptions, this.imports); + sourceMapBuilder = new SourceMapBuilder(options.sourceMap); + result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports); } else { result.css = evaldRoot.toCSS(toCSSOptions); } @@ -33,14 +35,14 @@ ParseTree.prototype.toCSS = function(options) { if (options.plugins) { var postProcessors = options.plugins.getPostProcessors(); for(var i = 0; i < postProcessors.length; i++) { - // TODO - pass source maps // TODO - async - result.css = postProcessors[i].process(result.css, options, this.imports); + result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports }); } } if (options.sourceMap) { - result.map = options.sourceMap.getExternalSourceMap(); + result.map = sourceMapBuilder.getExternalSourceMap(); } return result; }; -module.exports = ParseTree; +return ParseTree; +}; diff --git a/lib/less/render.js b/lib/less/render.js index ad98a1997..182e91bcb 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,10 +1,9 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, contexts = require("./contexts"), getImportManager = require("./imports"), - Parser = require('./parser/parser'), - ParseTree = require("./parse-tree"); + Parser = require('./parser/parser'); -module.exports = function(environment) { +module.exports = function(environment, ParseTree) { var render = function (input, options, callback) { options = options || {}; diff --git a/test/less-test.js b/test/less-test.js index 8719ac9c9..8ea848ac5 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -114,7 +114,7 @@ module.exports = function() { options.sourceMapBasepath = path.join(process.cwd(), "test/less"); options.sourceMapRootpath = "testweb/"; // TODO seperate options? - options.sourceMap = new less.SourceMapBuilder(options); + options.sourceMap = options; } options.getVars = function(file) { From c07531a46fe147519e663220a3efb3073320ce5b Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 20 Sep 2014 02:59:24 +0100 Subject: [PATCH 096/161] Fix the sourcemap options to default to the correct directories --- bin/lessc | 60 ++++++++++++++++++++++------------- lib/less/source-map-output.js | 2 +- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/bin/lessc b/bin/lessc index 18b42f7d5..8c485b950 100755 --- a/bin/lessc +++ b/bin/lessc @@ -29,6 +29,7 @@ var options = { modifyVariables: '', urlArgs: '' }; +var sourceMapOptions = {}; var continueProcessing = true, currentErrorcode; @@ -150,20 +151,19 @@ args = args.filter(function (arg) { } break; case 'source-map': - if (!match[2]) { - options.sourceMap = true; - } else { - options.sourceMap = match[2]; + options.sourceMap = true; + if (match[2]) { + sourceMapOptions.sourceMapFilename = match[2]; } break; case 'source-map-rootpath': if (checkArgFunc(arg, match[2])) { - options.sourceMapRootpath = match[2]; + sourceMapOptions.sourceMapRootpath = match[2]; } break; case 'source-map-basepath': if (checkArgFunc(arg, match[2])) { - options.sourceMapBasepath = match[2]; + sourceMapOptions.sourceMapBasepath = match[2]; } break; case 'source-map-map-inline': @@ -171,11 +171,11 @@ args = args.filter(function (arg) { options.sourceMap = true; break; case 'source-map-less-inline': - options.outputSourceFiles = true; + sourceMapOptions.outputSourceFiles = true; break; case 'source-map-url': if (checkArgFunc(arg, match[2])) { - options.sourceMapURL = match[2]; + sourceMapOptions.sourceMapURL = match[2]; } break; case 'rp': @@ -249,22 +249,40 @@ if (input && input != '-') { var output = args[2]; var outputbase = args[2]; if (output) { - options.sourceMapOutputFilename = output; output = path.resolve(process.cwd(), output); if (warningMessages) { console.log(warningMessages); } } -options.sourceMapBasepath = options.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); +sourceMapOptions.sourceMapBasepath = options.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); -if (options.sourceMap === true) { - if (!output && !sourceMapFileInline) { - console.log("the sourcemap option only has an optional filename if the css filename is given"); - return; +if (options.sourceMap) { + if (!sourceMapOptions.sourceMapFilename) { + if (!output && !sourceMapFileInline) { + console.log("the sourcemap option only has an optional filename if the css filename is given"); + console.log("consider adding --source-map-map-inline which embeds the sourcemap into the css"); + return; + } + // its in the same directory, so always just the basename + sourceMapOptions.sourceMapOutputFilename = path.basename(output); + sourceMapOptions.sourceMapFullFilename = output + ".map"; + // its in the same directory, so always just the basename + sourceMapOptions.sourceMapFilename = path.basename(sourceMapOptions.sourceMapFullFilename); + } else if (options.sourceMap && !sourceMapFileInline) { + var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFilename), + mapDir = path.dirname(mapFilename), + outputDir = path.dirname(output); + // find the path from the map to the output file + sourceMapOptions.sourceMapOutputFilename = path.join( + path.relative(mapDir, outputDir), path.basename(output)); + + // make the sourcemap filename point to the sourcemap relative to the css file output directory + sourceMapOptions.sourceMapFullFilename = sourceMapOptions.sourceMapFilename; + sourceMapOptions.sourceMapFilename = path.join( + path.relative(outputDir, mapDir), path.basename(sourceMapOptions.sourceMapFullFilename)); + console.log(sourceMapOptions.sourceMapFile); } - options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map"; - options.sourceMap = path.basename(options.sourceMapFullFilename); } if (! input) { @@ -299,7 +317,7 @@ if (options.depends) { if (!sourceMapFileInline) { var writeSourceMap = function(output) { - var filename = options.sourceMapFullFilename || options.sourceMapFilename; + var filename = sourceMapOptions.sourceMapFullFilename || sourceMapOptions.sourceMapFilename; ensureDirectory(filename); fs.writeFileSync(filename, output, 'utf8'); }; @@ -338,13 +356,11 @@ var parseLessFile = function (e, data) { if (options.lint) { options.sourceMap = false; } - options.sourceMapFilename = options.sourceMap; - options.sourceMapRootpath = options.sourceMapRootpath || ""; - options.sourceMapFileInline = sourceMapFileInline; + sourceMapOptions.sourceMapRootpath = options.sourceMapRootpath || ""; + sourceMapOptions.sourceMapFileInline = sourceMapFileInline; if (options.sourceMap) { - // todo seperate options - no need to send them to less any more? - options.sourceMap = options; + options.sourceMap = sourceMapOptions; } options.plugins = pluginManager; diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 5f4c9de60..c8fd8073f 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -128,7 +128,7 @@ module.exports = function (environment) { if (!this._sourceMapFileInline) { this.sourceMap = sourceMapContent; } else { - sourceMapURL = "data:application/json;base64," + environment.encodeBase64(sourceMapContent); + sourceMapURL = "data:application/json;base64," + environment.encodeBase64(null, sourceMapContent); } if (sourceMapURL) { From 40e0b212a6e7161973d558ecb9bdf214a11f1a06 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 20 Sep 2014 03:26:06 +0100 Subject: [PATCH 097/161] simplify sourcemapfilename in lessc and expose the output filename --- bin/lessc | 9 ++++----- lib/less/source-map-builder.js | 3 +++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/bin/lessc b/bin/lessc index 8c485b950..4032a4d7d 100755 --- a/bin/lessc +++ b/bin/lessc @@ -153,7 +153,7 @@ args = args.filter(function (arg) { case 'source-map': options.sourceMap = true; if (match[2]) { - sourceMapOptions.sourceMapFilename = match[2]; + sourceMapOptions.sourceMapFullFilename = match[2]; } break; case 'source-map-rootpath': @@ -258,7 +258,7 @@ if (output) { sourceMapOptions.sourceMapBasepath = options.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); if (options.sourceMap) { - if (!sourceMapOptions.sourceMapFilename) { + if (!sourceMapOptions.sourceMapFullFilename) { if (!output && !sourceMapFileInline) { console.log("the sourcemap option only has an optional filename if the css filename is given"); console.log("consider adding --source-map-map-inline which embeds the sourcemap into the css"); @@ -270,7 +270,7 @@ if (options.sourceMap) { // its in the same directory, so always just the basename sourceMapOptions.sourceMapFilename = path.basename(sourceMapOptions.sourceMapFullFilename); } else if (options.sourceMap && !sourceMapFileInline) { - var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFilename), + var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFullFilename), mapDir = path.dirname(mapFilename), outputDir = path.dirname(output); // find the path from the map to the output file @@ -278,7 +278,6 @@ if (options.sourceMap) { path.relative(mapDir, outputDir), path.basename(output)); // make the sourcemap filename point to the sourcemap relative to the css file output directory - sourceMapOptions.sourceMapFullFilename = sourceMapOptions.sourceMapFilename; sourceMapOptions.sourceMapFilename = path.join( path.relative(outputDir, mapDir), path.basename(sourceMapOptions.sourceMapFullFilename)); console.log(sourceMapOptions.sourceMapFile); @@ -317,7 +316,7 @@ if (options.depends) { if (!sourceMapFileInline) { var writeSourceMap = function(output) { - var filename = sourceMapOptions.sourceMapFullFilename || sourceMapOptions.sourceMapFilename; + var filename = sourceMapOptions.sourceMapFullFilename; ensureDirectory(filename); fs.writeFileSync(filename, output, 'utf8'); }; diff --git a/lib/less/source-map-builder.js b/lib/less/source-map-builder.js index 68df61881..826533e22 100644 --- a/lib/less/source-map-builder.js +++ b/lib/less/source-map-builder.js @@ -39,6 +39,9 @@ module.exports = function (SourceMapOutput) { SourceMapBuilder.prototype.getSourceMapURL = function() { return this.sourceMapURL; }; + SourceMapBuilder.prototype.getOutputFilename = function() { + return this.options.sourceMapOutputFilename; + }; return SourceMapBuilder; }; From a53be80014a6329b4545d3c6d68e13c7184f9846 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 21 Sep 2014 16:36:06 +0100 Subject: [PATCH 098/161] make the import manager one instance per render --- lib/less/imports.js | 140 +++++++++++++++++++++++--------------------- lib/less/index.js | 6 +- lib/less/render.js | 5 +- 3 files changed, 78 insertions(+), 73 deletions(-) diff --git a/lib/less/imports.js b/lib/less/imports.js index 3cb9cfb2b..a5fcdd498 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -1,85 +1,91 @@ var contexts = require("./contexts"), Parser = require('./parser/parser'); -var getImportManager = module.exports = function(environment, env) { - var rootFilename = env && env.filename; - return { - paths: env.paths || [], // Search paths, when importing - queue: [], // Files which haven't been imported yet - files: env.files, // Holds the imported parse trees - contents: env.contents, // Holds the imported file contents - contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less - mime: env.mime, // MIME type of .less files - error: null, // Error in parsing/evaluating an import - getAbsolutePath: function(env, filename) { - // proxy needed for "DebugInfo" - // I hope one day we can remove this function - return environment.getAbsolutePath(env, filename); - }, - push: function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; - this.queue.push(path); +// TODO - why does environment need env passed everywhere? +// Now we have one import manager per parse, can we move things from env to the import manager +// and then move the import manager onto env (if required there - if not, keep seperate) - var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue +module.exports = function(environment) { + var ImportManager = function(env) { + this.rootFilename = env && env.filename; + this.paths = env.paths || []; // Search paths, when importing + this.queue = []; // Files which haven't been imported yet + this.files = env.files; // Holds the imported parse trees + this.contents = env.contents; // Holds the imported file contents + this.contentsIgnoredChars = env.contentsIgnoredChars; // lines inserted, not in the original less + this.mime = env.mime; + this.error = null; + this.env = env; + }; + ImportManager.prototype.getAbsolutePath = function(env, filename) { + // proxy needed for "DebugInfo" + // I hope one day we can remove this function + return environment.getAbsolutePath(env, filename); + }; + ImportManager.prototype.push = function (path, currentFileInfo, importOptions, callback) { + var parserImports = this; + this.queue.push(path); - var importedPreviously = fullPath === rootFilename; + var fileParsedFunc = function (e, root, fullPath) { + parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - parserImports.files[fullPath] = root; // Store the root + var importedPreviously = fullPath === parserImports.rootFilename; - if (e && !parserImports.error) { parserImports.error = e; } + parserImports.files[fullPath] = root; // Store the root - callback(e, root, importedPreviously, fullPath); - }; + if (e && !parserImports.error) { parserImports.error = e; } - var newFileInfo = { - relativeUrls: env.relativeUrls, - entryPath: currentFileInfo.entryPath, - rootpath: currentFileInfo.rootpath, - rootFilename: currentFileInfo.rootFilename - }; + callback(e, root, importedPreviously, fullPath); + }; - environment.loadFile(env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { - if (e) { - fileParsedFunc(e); - return; - } + var newFileInfo = { + relativeUrls: this.env.relativeUrls, + entryPath: currentFileInfo.entryPath, + rootpath: currentFileInfo.rootpath, + rootFilename: currentFileInfo.rootFilename + }; - // Pass on an updated rootpath if path of imported file is relative and file - // is in a (sub|sup) directory - // - // Examples: - // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', - // then rootpath should become 'less/module/nav/' - // - If path of imported file is '../mixins.less' and rootpath is 'less/', - // then rootpath should become 'less/../' - newFileInfo.currentDirectory = environment.getPath(env, resolvedFilename); - if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = environment.join((env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); - if (!environment.isPathAbsolute(env, newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) { - newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath); - } + environment.loadFile(this.env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { + if (e) { + fileParsedFunc(e); + return; + } + + // Pass on an updated rootpath if path of imported file is relative and file + // is in a (sub|sup) directory + // + // Examples: + // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', + // then rootpath should become 'less/module/nav/' + // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // then rootpath should become 'less/../' + newFileInfo.currentDirectory = environment.getPath(parserImports.env, resolvedFilename); + if(newFileInfo.relativeUrls) { + newFileInfo.rootpath = environment.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!environment.isPathAbsolute(parserImports.env, newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath); } - newFileInfo.filename = resolvedFilename; + } + newFileInfo.filename = resolvedFilename; - var newEnv = new contexts.parseEnv(env); + var newEnv = new contexts.parseEnv(parserImports.env); - newEnv.currentFileInfo = newFileInfo; - newEnv.processImports = false; - newEnv.contents[resolvedFilename] = contents; + newEnv.currentFileInfo = newFileInfo; + newEnv.processImports = false; + newEnv.contents[resolvedFilename] = contents; - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } + if (currentFileInfo.reference || importOptions.reference) { + newFileInfo.reference = true; + } - if (importOptions.inline) { - fileParsedFunc(null, contents, resolvedFilename); - } else { - new(Parser)(newEnv, getImportManager(environment, env)).parse(contents, function (e, root) { - fileParsedFunc(e, root, resolvedFilename); - }); - } - }); - } + if (importOptions.inline) { + fileParsedFunc(null, contents, resolvedFilename); + } else { + new(Parser)(newEnv, parserImports).parse(contents, function (e, root) { + fileParsedFunc(e, root, resolvedFilename); + }); + } + }); }; + return ImportManager; }; diff --git a/lib/less/index.js b/lib/less/index.js index 5b45e6529..5b1f5cefc 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,5 +1,5 @@ module.exports = function(environment) { - var SourceMapOutput, SourceMapBuilder, ParseTree; + var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager; var less = { version: [2, 0, 0], @@ -13,8 +13,8 @@ module.exports = function(environment) { SourceMapOutput: (SourceMapOutput = require('./source-map-output.js')(environment)), SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder.js')(SourceMapOutput)), ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)), - render: require("./render")(environment, ParseTree), - getImportManager: require('./imports'), // TODO: change to class? add static ways of replacing file-manager? + ImportManager: (ImportManager = require('./imports')(environment)), + render: require("./render")(environment, ParseTree, ImportManager), LessError: require('./less-error'), transformTree: require('./transform-tree'), utils: require('./utils'), diff --git a/lib/less/render.js b/lib/less/render.js index 182e91bcb..5be9f5bf7 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,9 +1,8 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, contexts = require("./contexts"), - getImportManager = require("./imports"), Parser = require('./parser/parser'); -module.exports = function(environment, ParseTree) { +module.exports = function(environment, ParseTree, ImportManager) { var render = function (input, options, callback) { options = options || {}; @@ -22,7 +21,7 @@ module.exports = function(environment, ParseTree) { }); } else { var env = new contexts.parseEnv(options), - imports = getImportManager(environment, env); + imports = new ImportManager(env); var parser = new(Parser)(env, imports); From fb6c879cc493e41ba91027eb91dfd2600163479c Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 21 Sep 2014 16:46:44 +0100 Subject: [PATCH 099/161] remove env from all environment calls except the ones that actually need it --- lib/less-browser/environment.js | 16 +++++++++------- lib/less-browser/index.js | 4 ++-- lib/less-node/environment.js | 28 +++++++++++++++------------- lib/less/environment/api.js | 15 ++++++--------- lib/less/functions/data-uri.js | 6 +++--- lib/less/functions/svg.js | 2 +- lib/less/imports.js | 11 +++++------ lib/less/parser/parser.js | 2 +- lib/less/source-map-output.js | 2 +- 9 files changed, 43 insertions(+), 43 deletions(-) diff --git a/lib/less-browser/environment.js b/lib/less-browser/environment.js index 2e045c732..5f65bd485 100644 --- a/lib/less-browser/environment.js +++ b/lib/less-browser/environment.js @@ -23,11 +23,11 @@ function getXMLHttpRequest() { return { // make generic but overriddable - warn: function warn(env, msg) { + warn: function warn(msg) { console.warn(msg); }, // make generic but overriddable - getPath: function getPath(env, filename) { + getPath: function getPath(filename) { var j = filename.lastIndexOf('/'); if (j < 0) { j = filename.lastIndexOf('\\'); @@ -38,7 +38,7 @@ return { return filename.slice(0, j + 1); }, // make generic but overriddable - isPathAbsolute: function isPathAbsolute(env, filename) { + isPathAbsolute: function isPathAbsolute(filename) { return /^(?:[a-z-]+:|\/|\\)/i.test(filename); }, alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { @@ -168,17 +168,19 @@ return { handleResponse(xhr, callback, errback); } }, - loadFile: function loadFile(env, filename, currentDirectory, callback) { - if (currentDirectory && !this.isPathAbsolute(env, filename)) { + loadFile: function loadFile(filename, currentDirectory, options, callback) { + if (currentDirectory && !this.isPathAbsolute(filename)) { filename = currentDirectory + filename; } + options = options || {}; + // sheet may be set to the stylesheet for the initial load or a collection of properties including // some env variables for imports var hrefParts = this.extractUrlParts(filename, window.location.href); var href = hrefParts.url; - if (env.useFileCache && fileCache[href]) { + if (options.useFileCache && fileCache[href]) { try { var lessText = fileCache[href]; callback(null, lessText, href, { lastModified: new Date() }); @@ -188,7 +190,7 @@ return { return; } - this.doXHR(href, env.mime, function doXHRCallback(data, lastModified) { + this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) { // per file cache fileCache[href] = data; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index a767c60e4..36c4cb458 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -377,10 +377,10 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { instanceOptions.useFileCache = true; } - less.environment.loadFile(instanceOptions, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) { + less.environment.loadFile(sheet.href, null, instanceOptions, function loadInitialFileCallback(e, data, path, webInfo) { var newFileInfo = { - currentDirectory: less.environment.getPath(instanceOptions, path), + currentDirectory: less.environment.getPath(path), filename: path, rootFilename: path, relativeUrls: instanceOptions.relativeUrls}; diff --git a/lib/less-node/environment.js b/lib/less-node/environment.js index 3340091e2..c22a58311 100644 --- a/lib/less-node/environment.js +++ b/lib/less-node/environment.js @@ -5,25 +5,25 @@ var path = require('path'), isUrlRe = /^(?:https?:)?\/\//i; module.exports = { - warn: function(env, msg) { + warn: function(msg) { console.warn(msg); }, - encodeBase64: function encodeBase64(env, str) { + encodeBase64: function encodeBase64(str) { return new Buffer(str).toString('base64'); }, - supportsDataURI: function(env) { + supportsDataURI: function() { return true; }, - mimeLookup: function (env, filename) { + mimeLookup: function (filename) { return require('mime').lookup(filename); }, - charsetLookup: function (env, mime) { + charsetLookup: function (mime) { return require('mime').charsets.lookup(mime); }, readFileSync: function (filename) { return require("fs").readFileSync(filename); }, - getPath: function (env, filename) { + getPath: function (filename) { var j = filename.lastIndexOf('/'); if (j < 0) { j = filename.lastIndexOf('\\'); @@ -33,10 +33,10 @@ module.exports = { } return filename.slice(0, j + 1); }, - isPathAbsolute: function(env, filename) { + isPathAbsolute: function(filename) { return (/^(?:[a-z-]+:|\/|\\)/i).test(filename); }, - getAbsolutePath: function getAbsolutePath(env, filename) { + getAbsolutePath: function getAbsolutePath(filename) { return require('path').resolve(filename); }, getSourceMapGenerator: function getSourceMapGenerator() { @@ -128,11 +128,13 @@ module.exports = { returner.url = returner.fileUrl + (urlParts[5] || ""); return returner; }, - loadFile: function(env, filename, currentDirectory, callback) { + loadFile: function(filename, currentDirectory, options, callback) { var fullFilename, data, isUrl = isUrlRe.test( filename ); + options = options || {}; + if (isUrl || isUrlRe.test(currentDirectory)) { if (request === undefined) { try { request = require('request'); } @@ -151,7 +153,7 @@ module.exports = { urlStr = urlObj.format(); } - request.get({uri: urlStr, strictSSL: !env.insecure }, function (error, res, body) { + request.get({uri: urlStr, strictSSL: !options.insecure }, function (error, res, body) { if (error) { callback({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" }); } @@ -160,7 +162,7 @@ module.exports = { return; } if (!body) { - this.warn( env, 'Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); + this.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); } fullFilename = urlStr; callback(null, body, fullFilename); @@ -168,10 +170,10 @@ module.exports = { } else { var paths = [currentDirectory]; - if (env.paths) paths.push.apply(paths, env.paths); + if (options.paths) paths.push.apply(paths, options.paths); if (paths.indexOf('.') === -1) paths.push('.'); - if (env.syncImport) { + if (options.syncImport) { for (var i = 0; i < paths.length; i++) { try { fullFilename = path.join(paths[i], filename); diff --git a/lib/less/environment/api.js b/lib/less/environment/api.js index ef3dee294..90a71fc7a 100644 --- a/lib/less/environment/api.js +++ b/lib/less/environment/api.js @@ -1,42 +1,39 @@ module.exports = { /** * Warns the user about something - * @param {Object} env - the environment or options object * @param {String} msg - the message about the warning */ - warn: function(env, msg) { + warn: function(msg) { }, /** * gets the path from the filename, e.g. "http://wwe.files.com/ha/ha.less" would return * "http://wwe.files.com/ha/" * If the filename is a file e.g. "file.less" it should return the empty string "" - * @param {Object} env - the environment or options object * @param {String} filename - the filename to extract the path. * @returns {String} */ - getPath: function (env, filename) { + getPath: function (filename) { }, /** * Returns whether the path is absolute, e.g. "/file.less" = true, "file.less" = false - * @param {Object} env - the environment or options object * @param {String} filename - the filename * @returns {Boolean} */ - isPathAbsolute: function(env, filename) { + isPathAbsolute: function(filename) { }, /** * Loads a file for an import aynscronously (or syncronously) - * @param {Object} env - the environment or options object * @param {String} filename - the filename * @param {String} currentDirectory - the current directory we are in + * @param {Object} options - the environment or options object * @param {Function} callback - a function to callback when finished, * taking the format callback(error, contents, fullfilename, reserved) * where error is { type: {string}, message: {string} }, contents is {string} and fullfilename is {string} * for reserved, see less-browser/index.js which uses this argument for cache information * @returns {Boolean} */ - loadFile: function(env, filename, currentDirectory, callback) { + loadFile: function(filename, currentDirectory, options, callback) { }, - supportsDataURI: function(env) { + supportsDataURI: function() { } }; diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index 3112d80d5..8a4ee5e06 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -5,7 +5,7 @@ module.exports = function(environment) { functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { - if (!environment.supportsDataURI(this.env)) { + if (!environment.supportsDataURI()) { return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env); } @@ -36,10 +36,10 @@ module.exports = function(environment) { // detect the mimetype if not given if (arguments.length < 2) { - mimetype = environment.mimeLookup(this.env, filePath); + mimetype = environment.mimeLookup(filePath); // use base 64 unless it's an ASCII or UTF-8 format - var charset = environment.charsetLookup(this.env, mimetype); + var charset = environment.charsetLookup(mimetype); useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; if (useBase64) { mimetype += ';base64'; } } diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js index 5bd8fc68b..ba803020c 100644 --- a/lib/less/functions/svg.js +++ b/lib/less/functions/svg.js @@ -71,7 +71,7 @@ module.exports = function(environment) { if (useBase64) { try { - returner = environment.encodeBase64(this.env, returner); + returner = environment.encodeBase64(returner); } catch(e) { useBase64 = false; } diff --git a/lib/less/imports.js b/lib/less/imports.js index a5fcdd498..bcd5a6cee 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -1,7 +1,6 @@ var contexts = require("./contexts"), Parser = require('./parser/parser'); -// TODO - why does environment need env passed everywhere? // Now we have one import manager per parse, can we move things from env to the import manager // and then move the import manager onto env (if required there - if not, keep seperate) @@ -17,10 +16,10 @@ module.exports = function(environment) { this.error = null; this.env = env; }; - ImportManager.prototype.getAbsolutePath = function(env, filename) { + ImportManager.prototype.getAbsolutePath = function(filename) { // proxy needed for "DebugInfo" // I hope one day we can remove this function - return environment.getAbsolutePath(env, filename); + return environment.getAbsolutePath(filename); }; ImportManager.prototype.push = function (path, currentFileInfo, importOptions, callback) { var parserImports = this; @@ -45,7 +44,7 @@ module.exports = function(environment) { rootFilename: currentFileInfo.rootFilename }; - environment.loadFile(this.env, path, currentFileInfo.currentDirectory, function loadFileCallback(e, contents, resolvedFilename) { + environment.loadFile(path, currentFileInfo.currentDirectory, this.env, function loadFileCallback(e, contents, resolvedFilename) { if (e) { fileParsedFunc(e); return; @@ -59,10 +58,10 @@ module.exports = function(environment) { // then rootpath should become 'less/module/nav/' // - If path of imported file is '../mixins.less' and rootpath is 'less/', // then rootpath should become 'less/../' - newFileInfo.currentDirectory = environment.getPath(parserImports.env, resolvedFilename); + newFileInfo.currentDirectory = environment.getPath(resolvedFilename); if(newFileInfo.relativeUrls) { newFileInfo.rootpath = environment.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); - if (!environment.isPathAbsolute(parserImports.env, newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) { + if (!environment.isPathAbsolute(newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) { newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath); } } diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index d62322385..5632e9d89 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -73,7 +73,7 @@ var Parser = function Parser(env, imports) { function getDebugInfo(index) { var filename = env.currentFileInfo.filename; - filename = imports.getAbsolutePath(env, filename); + filename = imports.getAbsolutePath(filename); return { lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index c8fd8073f..5f4c9de60 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -128,7 +128,7 @@ module.exports = function (environment) { if (!this._sourceMapFileInline) { this.sourceMap = sourceMapContent; } else { - sourceMapURL = "data:application/json;base64," + environment.encodeBase64(null, sourceMapContent); + sourceMapURL = "data:application/json;base64," + environment.encodeBase64(sourceMapContent); } if (sourceMapURL) { From 0c1fb3248b0246e128f4a67a9b212c4b321acec5 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 21 Sep 2014 16:48:35 +0100 Subject: [PATCH 100/161] Add another todo --- lib/less-node/environment.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/less-node/environment.js b/lib/less-node/environment.js index c22a58311..f4abb06fd 100644 --- a/lib/less-node/environment.js +++ b/lib/less-node/environment.js @@ -128,6 +128,7 @@ module.exports = { returner.url = returner.fileUrl + (urlParts[5] || ""); return returner; }, + // consider if we really want this in the environment or whether all or some of this should be moved to (for node) built in import manager plugins? loadFile: function(filename, currentDirectory, options, callback) { var fullFilename, data, From ff72e24ba5257173431b9af87a3a3ac4caf610fb Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 22 Sep 2014 20:31:25 +0100 Subject: [PATCH 101/161] Fix browser if no less object specified --- lib/less-browser/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 36c4cb458..6bfae4f7b 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -28,7 +28,7 @@ function log(str, level) { */ var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), - options = window.less, + options = window.less || {}, environment = require("./environment")(options, isFileProtocol, log, logLevel); window.less = less = require('../less')(environment); From b3b8b278bda85d752297ab66cf9858534aa4c054 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 22 Sep 2014 20:32:46 +0100 Subject: [PATCH 102/161] import managaer related simplifications --- bin/lessc | 1 + lib/less/contexts.js | 2 -- lib/less/imports.js | 11 ++++++----- lib/less/parser/parser-input.js | 15 ++++----------- lib/less/parser/parser.js | 9 ++++++++- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bin/lessc b/bin/lessc index 4032a4d7d..c170d00a1 100755 --- a/bin/lessc +++ b/bin/lessc @@ -258,6 +258,7 @@ if (output) { sourceMapOptions.sourceMapBasepath = options.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); if (options.sourceMap) { + sourceMapOptions.sourceMapInputFilename = input; if (!sourceMapOptions.sourceMapFullFilename) { if (!output && !sourceMapFileInline) { console.log("the sourcemap option only has an optional filename if the css filename is given"); diff --git a/lib/less/contexts.js b/lib/less/contexts.js index c79349e41..0c9b6908b 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -13,7 +13,6 @@ var copyFromOriginal = function copyFromOriginal(original, destination, properti var parseCopyProperties = [ 'paths', // option - unmodified - paths to search for imports on - 'files', // list of files that have been imported, used for import-once 'contents', // map - filename to contents of all the files 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore 'relativeUrls', // option - whether to adjust URL's to be relative @@ -44,7 +43,6 @@ contexts.parseEnv = function(options) { if (!this.contents) { this.contents = {}; } if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (!this.files) { this.files = {}; } if (typeof this.paths === "string") { this.paths = [this.paths]; } diff --git a/lib/less/imports.js b/lib/less/imports.js index bcd5a6cee..ebcfc04e6 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -8,13 +8,14 @@ module.exports = function(environment) { var ImportManager = function(env) { this.rootFilename = env && env.filename; this.paths = env.paths || []; // Search paths, when importing - this.queue = []; // Files which haven't been imported yet - this.files = env.files; // Holds the imported parse trees this.contents = env.contents; // Holds the imported file contents this.contentsIgnoredChars = env.contentsIgnoredChars; // lines inserted, not in the original less this.mime = env.mime; this.error = null; this.env = env; + // Deprecated? Unused outside of here, could be useful. + this.queue = []; // Files which haven't been imported yet + this.files = []; // Holds the imported parse trees. }; ImportManager.prototype.getAbsolutePath = function(filename) { // proxy needed for "DebugInfo" @@ -28,13 +29,13 @@ module.exports = function(environment) { var fileParsedFunc = function (e, root, fullPath) { parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue - var importedPreviously = fullPath === parserImports.rootFilename; + var importedEqualsRoot = fullPath === parserImports.rootFilename; - parserImports.files[fullPath] = root; // Store the root + parserImports.files[fullPath] = root; if (e && !parserImports.error) { parserImports.error = e; } - callback(e, root, importedPreviously, fullPath); + callback(e, root, importedEqualsRoot, fullPath); }; var newFileInfo = { diff --git a/lib/less/parser/parser-input.js b/lib/less/parser/parser-input.js index fc0bf56b3..39f1e4c64 100644 --- a/lib/less/parser/parser-input.js +++ b/lib/less/parser/parser-input.js @@ -1,5 +1,5 @@ -var chunker = require('./chunker'), - LessError = require('../less-error'); +var chunker = require('./chunker'); + module.exports = function() { var input, // LeSS input string j, // current chunk @@ -211,7 +211,7 @@ module.exports = function() { return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; }; - parserInput.start = function(str, chunkInput, env) { + parserInput.start = function(str, chunkInput, failFunction) { input = str; parserInput.i = j = currentPos = furthest = 0; @@ -226,14 +226,7 @@ module.exports = function() { // this is officially deprecated but can be switched on via an option // in the case it causes too much performance issues. if (chunkInput) { - chunks = chunker(str, function fail(msg, index) { - throw new(LessError)({ - index: index, - type: 'Parse', - message: msg, - filename: env.currentFileInfo.filename - }, env); //TODO works because env contains contents like importManager - }); + chunks = chunker(str, failFunction); } else { chunks = [str]; } diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 5632e9d89..1f7752106 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -113,7 +113,14 @@ var Parser = function Parser(env, imports) { // with the `root` property set to true, so no `{}` are // output. The callback is called when the input is parsed. try { - parserInput.start(str, env.chunkInput, env); + parserInput.start(str, env.chunkInput, function fail(msg, index) { + throw new(LessError)({ + index: index, + type: 'Parse', + message: msg, + filename: env.currentFileInfo.filename + }, imports); + }); root = new(tree.Ruleset)(null, this.parsers.primary()); root.root = true; From 3f38581ce31b4882ca5667e104b7faffc5eb3215 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 22 Sep 2014 20:44:01 +0100 Subject: [PATCH 103/161] move some contexts into the import manager --- lib/less/contexts.js | 10 +++++----- lib/less/imports.js | 9 +++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 0c9b6908b..40922961d 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -11,10 +11,13 @@ var copyFromOriginal = function copyFromOriginal(original, destination, properti } }; +/* + parseEnv is 2 things + 1. a set of options + 2. The context of the current file information + */ var parseCopyProperties = [ 'paths', // option - unmodified - paths to search for imports on - 'contents', // map - filename to contents of all the files - 'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore 'relativeUrls', // option - whether to adjust URL's to be relative 'rootpath', // option - rootpath to append to URL's 'strictImports', // option - @@ -41,9 +44,6 @@ var parseCopyProperties = [ contexts.parseEnv = function(options) { copyFromOriginal(options, this, parseCopyProperties); - if (!this.contents) { this.contents = {}; } - if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; } - if (typeof this.paths === "string") { this.paths = [this.paths]; } if (!this.currentFileInfo) { diff --git a/lib/less/imports.js b/lib/less/imports.js index ebcfc04e6..d3609648f 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -1,15 +1,12 @@ var contexts = require("./contexts"), Parser = require('./parser/parser'); -// Now we have one import manager per parse, can we move things from env to the import manager -// and then move the import manager onto env (if required there - if not, keep seperate) - module.exports = function(environment) { var ImportManager = function(env) { this.rootFilename = env && env.filename; this.paths = env.paths || []; // Search paths, when importing - this.contents = env.contents; // Holds the imported file contents - this.contentsIgnoredChars = env.contentsIgnoredChars; // lines inserted, not in the original less + this.contents = {}; // map - filename to contents of all the files + this.contentsIgnoredChars = {}; // map - filename to lines at the begining of each file to ignore this.mime = env.mime; this.error = null; this.env = env; @@ -72,7 +69,7 @@ module.exports = function(environment) { newEnv.currentFileInfo = newFileInfo; newEnv.processImports = false; - newEnv.contents[resolvedFilename] = contents; + parserImports.contents[resolvedFilename] = contents; if (currentFileInfo.reference || importOptions.reference) { newFileInfo.reference = true; From 823ab221dd62edc767ef235d6a2f0c1d57b10c58 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Mon, 22 Sep 2014 21:24:48 +0100 Subject: [PATCH 104/161] further simplifications to the parse context --- lib/less-browser/index.js | 2 +- lib/less/contexts.js | 36 +++++--------------------- lib/less/imports.js | 17 +++++++++--- lib/less/parser/parser.js | 54 +++++++++++++++++++-------------------- lib/less/render.js | 20 +++++++++++++-- test/less-test.js | 18 +++++++------ 6 files changed, 75 insertions(+), 72 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 6bfae4f7b..59d68bb8b 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -409,7 +409,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { removeError(path); if (data) { - instanceOptions.currentFileInfo = newFileInfo; + instanceOptions.rootFileInfo = newFileInfo; less.render(data, instanceOptions) .then(function(result) { callback(null, result.css, data, sheet, webInfo, path); diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 40922961d..467cfe6c5 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -12,11 +12,10 @@ var copyFromOriginal = function copyFromOriginal(original, destination, properti }; /* - parseEnv is 2 things - 1. a set of options - 2. The context of the current file information + parseEnv is used whilst parsing */ var parseCopyProperties = [ + // options 'paths', // option - unmodified - paths to search for imports on 'relativeUrls', // option - whether to adjust URL's to be relative 'rootpath', // option - rootpath to append to URL's @@ -24,43 +23,20 @@ var parseCopyProperties = [ 'insecure', // option - whether to allow imports from insecure ssl hosts 'dumpLineNumbers', // option - whether to dump line numbers 'compress', // option - whether to compress - 'processImports', // option - whether to process imports. if false then imports will not be imported 'syncImport', // option - whether to import synchronously 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. 'mime', // browser only - mime type for sheet import 'useFileCache', // browser only - whether to use the per file session cache - 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc. + // context + 'processImports', // option & context - whether to process imports. if false then imports will not be imported. + // Used by the import manager to stop multiple import visitors being created. + 'reference' // Used to indicate that the contents are imported by reference ]; -//currentFileInfo = { -// 'relativeUrls' - option - whether to adjust URL's to be relative -// 'filename' - full resolved filename of current file -// 'rootpath' - path to append to normal URLs for this node -// 'currentDirectory' - path to the current file, absolute -// 'rootFilename' - filename of the base file -// 'entryPath' - absolute path to the entry file -// 'reference' - whether the file should not be output and only output parts that are referenced - contexts.parseEnv = function(options) { copyFromOriginal(options, this, parseCopyProperties); if (typeof this.paths === "string") { this.paths = [this.paths]; } - - if (!this.currentFileInfo) { - var filename = (options && options.filename) || "input"; - var entryPath = filename.replace(/[^\/\\]*$/, ""); - if (options) { - options.filename = null; - } - this.currentFileInfo = { - filename: filename, - relativeUrls: this.relativeUrls, - rootpath: (options && options.rootpath) || "", - currentDirectory: entryPath, - entryPath: entryPath, - rootFilename: filename - }; - } }; var evalCopyProperties = [ diff --git a/lib/less/imports.js b/lib/less/imports.js index d3609648f..e426c6dd3 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -2,8 +2,18 @@ var contexts = require("./contexts"), Parser = require('./parser/parser'); module.exports = function(environment) { - var ImportManager = function(env) { - this.rootFilename = env && env.filename; + + // FileInfo = { + // 'relativeUrls' - option - whether to adjust URL's to be relative + // 'filename' - full resolved filename of current file + // 'rootpath' - path to append to normal URLs for this node + // 'currentDirectory' - path to the current file, absolute + // 'rootFilename' - filename of the base file + // 'entryPath' - absolute path to the entry file + // 'reference' - whether the file should not be output and only output parts that are referenced + + var ImportManager = function(env, rootFileInfo) { + this.rootFilename = rootFileInfo.filename; this.paths = env.paths || []; // Search paths, when importing this.contents = {}; // map - filename to contents of all the files this.contentsIgnoredChars = {}; // map - filename to lines at the begining of each file to ignore @@ -67,7 +77,6 @@ module.exports = function(environment) { var newEnv = new contexts.parseEnv(parserImports.env); - newEnv.currentFileInfo = newFileInfo; newEnv.processImports = false; parserImports.contents[resolvedFilename] = contents; @@ -78,7 +87,7 @@ module.exports = function(environment) { if (importOptions.inline) { fileParsedFunc(null, contents, resolvedFilename); } else { - new(Parser)(newEnv, parserImports).parse(contents, function (e, root) { + new Parser(newEnv, parserImports, newFileInfo).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); }); } diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 1f7752106..85792d3e0 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -37,7 +37,7 @@ var LessError = require('../less-error'), // It also takes care of moving all the indices forwards. // // -var Parser = function Parser(env, imports) { +var Parser = function Parser(env, imports, fileInfo) { var parsers, parserInput = getParserInput(); @@ -63,7 +63,7 @@ var Parser = function Parser(env, imports) { throw new LessError( { index: parserInput.i, - filename: env.currentFileInfo.filename, + filename: fileInfo.filename, type: type || 'Syntax', message: msg }, @@ -72,7 +72,7 @@ var Parser = function Parser(env, imports) { } function getDebugInfo(index) { - var filename = env.currentFileInfo.filename; + var filename = fileInfo.filename; filename = imports.getAbsolutePath(filename); return { @@ -100,13 +100,13 @@ var Parser = function Parser(env, imports) { if (globalVars || (additionalData && additionalData.banner)) { preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; - imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length; + imports.contentsIgnoredChars[fileInfo.filename] = preText.length; } str = str.replace(/\r\n/g, '\n'); // Remove potential UTF Byte Order Mark str = preText + str.replace(/^\uFEFF/, '') + modifyVars; - imports.contents[env.currentFileInfo.filename] = str; + imports.contents[fileInfo.filename] = str; // Start with the primary rule. // The whole syntax tree is held under a Ruleset node, @@ -118,7 +118,7 @@ var Parser = function Parser(env, imports) { index: index, type: 'Parse', message: msg, - filename: env.currentFileInfo.filename + filename: fileInfo.filename }, imports); }); @@ -126,7 +126,7 @@ var Parser = function Parser(env, imports) { root.root = true; root.firstRoot = true; } catch (e) { - return callback(new LessError(e, imports, env.currentFileInfo.filename)); + return callback(new LessError(e, imports, fileInfo.filename)); } // If `i` is smaller than the `input.length - 1`, @@ -157,7 +157,7 @@ var Parser = function Parser(env, imports) { type: "Parse", message: message, index: endInfo.furthest, - filename: env.currentFileInfo.filename + filename: fileInfo.filename }, imports); } @@ -166,7 +166,7 @@ var Parser = function Parser(env, imports) { if (e) { if (!(e instanceof LessError)) { - e = new LessError(e, imports, env.currentFileInfo.filename); + e = new LessError(e, imports, fileInfo.filename); } return callback(e); @@ -261,7 +261,7 @@ var Parser = function Parser(env, imports) { comment: function () { if (parserInput.commentStore.length) { var comment = parserInput.commentStore.shift(); - return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, env.currentFileInfo); + return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo); } }, @@ -279,7 +279,7 @@ var Parser = function Parser(env, imports) { str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/); if (str) { - return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, env.currentFileInfo); + return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, fileInfo); } }, @@ -335,7 +335,7 @@ var Parser = function Parser(env, imports) { } parserInput.forget(); - return new(tree.Call)(name, args, index, env.currentFileInfo); + return new(tree.Call)(name, args, index, fileInfo); }, arguments: function () { var args = [], arg; @@ -404,7 +404,7 @@ var Parser = function Parser(env, imports) { expectChar(')'); return new(tree.URL)((value.value != null || value instanceof tree.Variable) ? - value : new(tree.Anonymous)(value), index, env.currentFileInfo); + value : new(tree.Anonymous)(value), index, fileInfo); }, // @@ -419,7 +419,7 @@ var Parser = function Parser(env, imports) { var name, index = parserInput.i; if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) { - return new(tree.Variable)(name, index, env.currentFileInfo); + return new(tree.Variable)(name, index, fileInfo); } }, @@ -428,7 +428,7 @@ var Parser = function Parser(env, imports) { var curly, index = parserInput.i; if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) { - return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo); + return new(tree.Variable)("@" + curly[1], index, fileInfo); } }, @@ -492,7 +492,7 @@ var Parser = function Parser(env, imports) { js = parserInput.$re(/^(~)?`([^`]*)`/); if (js) { - return new(tree.JavaScript)(js[2], Boolean(js[1]), index, env.currentFileInfo); + return new(tree.JavaScript)(js[2], Boolean(js[1]), index, fileInfo); } } }, @@ -591,7 +591,7 @@ var Parser = function Parser(env, imports) { if (!e) { break; } - elem = new(tree.Element)(c, e, elemIndex, env.currentFileInfo); + elem = new(tree.Element)(c, e, elemIndex, fileInfo); if (elements) { elements.push(elem); } else { elements = [ elem ]; } c = parserInput.$char('>'); } @@ -608,7 +608,7 @@ var Parser = function Parser(env, imports) { if (parsers.end()) { parserInput.forget(); - return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important); + return new(tree.mixin.Call)(elements, args, index, fileInfo, important); } } @@ -868,7 +868,7 @@ var Parser = function Parser(env, imports) { } } - if (e) { return new(tree.Element)(c, e, index, env.currentFileInfo); } + if (e) { return new(tree.Element)(c, e, index, fileInfo); } }, // @@ -943,7 +943,7 @@ var Parser = function Parser(env, imports) { } } - if (elements) { return new(tree.Selector)(elements, extendList, condition, index, env.currentFileInfo); } + if (elements) { return new(tree.Selector)(elements, extendList, condition, index, fileInfo); } if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } }, attribute: function () { @@ -1067,7 +1067,7 @@ var Parser = function Parser(env, imports) { if (value) { parserInput.forget(); // anonymous values absorb the end ';' which is reequired for them to work - return new (tree.Rule)(name, value, false, merge, startOfRule, env.currentFileInfo); + return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo); } } if (!tryValueFirst && !value) { @@ -1079,7 +1079,7 @@ var Parser = function Parser(env, imports) { if (value && this.end()) { parserInput.forget(); - return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo); + return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo); } else { parserInput.restore(); if (value && !tryAnonymous) { @@ -1123,7 +1123,7 @@ var Parser = function Parser(env, imports) { error("missing semi-colon or unrecognised media features on import"); } features = features && new(tree.Value)(features); - return new(tree.Import)(path, features, options, index, env.currentFileInfo); + return new(tree.Import)(path, features, options, index, fileInfo); } else { @@ -1180,7 +1180,7 @@ var Parser = function Parser(env, imports) { e = this.value(); if (parserInput.$char(')')) { if (p && e) { - nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, env.currentFileInfo, true))); + nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true))); } else if (e) { nodes.push(new(tree.Paren)(e)); } else { @@ -1231,7 +1231,7 @@ var Parser = function Parser(env, imports) { rules = this.block(); if (rules) { - media = new(tree.Media)(rules, features, parserInput.i, env.currentFileInfo); + media = new(tree.Media)(rules, features, parserInput.i, fileInfo); if (env.dumpLineNumbers) { media.debugInfo = debugInfo; } @@ -1334,7 +1334,7 @@ var Parser = function Parser(env, imports) { if (rules || (!hasBlock && value && parserInput.$char(';'))) { parserInput.forget(); - return new(tree.Directive)(name, value, rules, index, env.currentFileInfo, + return new(tree.Directive)(name, value, rules, index, fileInfo, env.dumpLineNumbers ? getDebugInfo(index) : null); } @@ -1572,7 +1572,7 @@ var Parser = function Parser(env, imports) { name[k] = (s.charAt(0) !== '@') ? new(tree.Keyword)(s) : new(tree.Variable)('@' + s.slice(2, -1), - index[k], env.currentFileInfo); + index[k], fileInfo); } return name; } diff --git a/lib/less/render.js b/lib/less/render.js index 5be9f5bf7..50d88999a 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -21,9 +21,25 @@ module.exports = function(environment, ParseTree, ImportManager) { }); } else { var env = new contexts.parseEnv(options), - imports = new ImportManager(env); + rootFileInfo; - var parser = new(Parser)(env, imports); + if (options.rootFileInfo) { + rootFileInfo = options.rootFileInfo; + } else { + var filename = options.filename || "input"; + var entryPath = filename.replace(/[^\/\\]*$/, ""); + rootFileInfo = { + filename: filename, + relativeUrls: env.relativeUrls, + rootpath: env.rootpath || "", + currentDirectory: entryPath, + entryPath: entryPath, + rootFilename: filename + }; + } + + var imports = new ImportManager(env, rootFileInfo); + var parser = new Parser(env, imports, rootFileInfo); return new PromiseConstructor(function (resolve, reject) { parser.parse(input, function (e, root) { diff --git a/test/less-test.js b/test/less-test.js index 8ea848ac5..b4aa02ac1 100644 --- a/test/less-test.js +++ b/test/less-test.js @@ -124,7 +124,15 @@ module.exports = function() { toCSS(options, path.join('test/less/', foldername + file), function (err, result) { if (verifyFunction) { - return verifyFunction(name, err, result.css, doReplacements, result.map); + return verifyFunction(name, err, result && result.css, doReplacements, result && result.map); + } + if (err) { + fail("ERROR: " + (err && err.message)); + if (isVerbose) { + process.stdout.write("\n"); + process.stdout.write(err.stack + "\n"); + } + return; } var css_name = name; if(nameModifier) { css_name = nameModifier(name); } @@ -133,13 +141,7 @@ module.exports = function() { css = css && doReplacements(css, 'test/less/' + foldername); if (result.css === css) { ok('OK'); } - else if (err) { - fail("ERROR: " + (err && err.message)); - if (isVerbose) { - process.stdout.write("\n"); - process.stdout.write(err.stack + "\n"); - } - } else { + else { difference("FAIL", css, result.css); } }); From d4415a567aed953b8e94f5b8fe7aafdee80e80b0 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 4 Oct 2014 13:50:30 +0100 Subject: [PATCH 105/161] Add an option to get the input filename --- lib/less/source-map-builder.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/less/source-map-builder.js b/lib/less/source-map-builder.js index 826533e22..3cd382f45 100644 --- a/lib/less/source-map-builder.js +++ b/lib/less/source-map-builder.js @@ -23,6 +23,7 @@ module.exports = function (SourceMapOutput) { var css = sourceMapOutput.toCSS(options); this.sourceMap = sourceMapOutput.sourceMap; this.sourceMapURL = sourceMapOutput.sourceMapURL; + this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename); return css; }; @@ -42,6 +43,9 @@ module.exports = function (SourceMapOutput) { SourceMapBuilder.prototype.getOutputFilename = function() { return this.options.sourceMapOutputFilename; }; + SourceMapBuilder.prototype.getInputFilename = function() { + return this.sourceMapInputFilename; + }; return SourceMapBuilder; }; From a51c8480efaf12d8071a5f5192770aa13cf3cc70 Mon Sep 17 00:00:00 2001 From: seven-phases-max Date: Sat, 4 Oct 2014 20:19:50 +0400 Subject: [PATCH 106/161] Fix interpolated selector match regression --- lib/less/tree/selector.js | 46 +++++++++--------------------- test/css/mixins-interpolated.css | 4 +++ test/less/mixins-interpolated.less | 6 ++++ 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index e27e70f01..993d23709 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -50,41 +50,23 @@ Selector.prototype.match = function (other) { return olen; // return number of matched elements }; -Selector.prototype.CacheElements = function(){ - var css = '', len, v, i; +Selector.prototype.CacheElements = function() { + if (this._elements) + return; + + var elements = this.elements.map( function(v) { + return v.combinator.value + (v.value.value || v.value); + }).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); - if( !this._elements ){ - - len = this.elements.length; - for(i = 0; i < len; i++){ - - v = this.elements[i]; - css += v.combinator.value; - - if( !v.value.value ){ - css += v.value; - continue; - } - - if( typeof v.value.value !== "string" ){ - css = ''; - break; - } - css += v.value.value; + if (elements) { + if (elements[0] === "&") { + elements.shift(); } - - this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); - - if (this._elements) { - if (this._elements[0] === "&") { - this._elements.shift(); - } - - } else { - this._elements = []; - } - + } else { + elements = []; } + + this._elements = elements; }; Selector.prototype.isJustParentSelector = function() { return !this.mediaEmpty && diff --git a/test/css/mixins-interpolated.css b/test/css/mixins-interpolated.css index 637b5b682..0d511fc67 100644 --- a/test/css/mixins-interpolated.css +++ b/test/css/mixins-interpolated.css @@ -1,3 +1,6 @@ +.123 { + a: 0; +} .foo { a: 1; } @@ -11,6 +14,7 @@ a: 4; } mi-test-a { + a: 0; a: 1; a: 2; a: 3; diff --git a/test/less/mixins-interpolated.less b/test/less/mixins-interpolated.less index 2e75e9805..b79fa89a6 100644 --- a/test/less/mixins-interpolated.less +++ b/test/less/mixins-interpolated.less @@ -1,8 +1,13 @@ +@a0: 123; @a1: foo; @a2: ~".foo"; @a4: ~"#foo"; +.@{a0} { + a: 0; +} + .@{a1} { a: 1; } @@ -20,6 +25,7 @@ } mi-test-a { + .123; .foo; #foo; } From 7a6b1e278b5a81ae6ca984e2d04c6146464162b0 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 4 Oct 2014 17:46:30 +0100 Subject: [PATCH 107/161] Pull out the file manager aspect of the environment so new file managers can be added dynamically --- bin/lessc | 1 - lib/less-browser/browser-import.js | 137 ++++++++++++ lib/less-browser/environment.js | 203 +---------------- lib/less-browser/index.js | 44 ++-- lib/less-node/environment.js | 204 ------------------ lib/less-node/file-import.js | 79 +++++++ lib/less-node/index.js | 7 +- lib/less-node/url-import.js | 52 +++++ lib/less/contexts.js | 3 +- lib/less/environment/abstract-file-manager.js | 111 ++++++++++ lib/less/environment/api.js | 23 ++ lib/less/environment/environment.js | 36 ++++ lib/less/functions/data-uri.js | 37 ++-- lib/less/imports.js | 92 ++++---- lib/less/index.js | 12 +- lib/less/parser/parser.js | 1 - lib/less/source-map-builder.js | 4 +- lib/less/tree/import.js | 1 - 18 files changed, 551 insertions(+), 496 deletions(-) create mode 100644 lib/less-browser/browser-import.js create mode 100644 lib/less-node/file-import.js create mode 100644 lib/less-node/url-import.js create mode 100644 lib/less/environment/abstract-file-manager.js create mode 100644 lib/less/environment/environment.js diff --git a/bin/lessc b/bin/lessc index c170d00a1..afb75c3d1 100755 --- a/bin/lessc +++ b/bin/lessc @@ -281,7 +281,6 @@ if (options.sourceMap) { // make the sourcemap filename point to the sourcemap relative to the css file output directory sourceMapOptions.sourceMapFilename = path.join( path.relative(outputDir, mapDir), path.basename(sourceMapOptions.sourceMapFullFilename)); - console.log(sourceMapOptions.sourceMapFile); } } diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js new file mode 100644 index 000000000..5a5ec3fe4 --- /dev/null +++ b/lib/less-browser/browser-import.js @@ -0,0 +1,137 @@ +/*global window, XMLHttpRequest */ + +module.exports = function(options, isFileProtocol, log, logLevel) { + +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, + AbstractFileManager = require("../less/environment/abstract-file-manager.js"); + +var fileCache = {}; + +//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load +// isFileProtocol is global + +function getXMLHttpRequest() { + if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { + return new XMLHttpRequest(); + } else { + try { + /*global ActiveXObject */ + return new ActiveXObject("Microsoft.XMLHTTP"); + } catch (e) { + log("browser doesn't support AJAX.", logLevel.errors); + return null; + } + } +}; + +var BrowserImport = function() { +}; + +BrowserImport.prototype = new AbstractFileManager(); + +BrowserImport.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() { + return true; +}; +BrowserImport.prototype.join = function (url, baseUrl) { + // diff between two paths to create a relative path + + var urlParts = this.extractUrlParts(url), + baseUrlParts = this.extractUrlParts(baseUrl), + i, max, urlDirectories, baseUrlDirectories, diff = ""; + if (urlParts.hostPart !== baseUrlParts.hostPart) { + return ""; + } + max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); + for(i = 0; i < max; i++) { + if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } + } + baseUrlDirectories = baseUrlParts.directories.slice(i); + urlDirectories = urlParts.directories.slice(i); + for(i = 0; i < baseUrlDirectories.length-1; i++) { + diff += "../"; + } + for(i = 0; i < urlDirectories.length-1; i++) { + diff += urlDirectories[i] + "/"; + } + return diff; +}; +BrowserImport.prototype.doXHR = function doXHR(url, type, callback, errback) { + + var xhr = getXMLHttpRequest(); + var async = isFileProtocol ? options.fileAsync : options.async; + + if (typeof(xhr.overrideMimeType) === 'function') { + xhr.overrideMimeType('text/css'); + } + log("XHR: Getting '" + url + "'", logLevel.debug); + xhr.open('GET', url, async); + xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); + xhr.send(null); + + function handleResponse(xhr, callback, errback) { + if (xhr.status >= 200 && xhr.status < 300) { + callback(xhr.responseText, + xhr.getResponseHeader("Last-Modified")); + } else if (typeof(errback) === 'function') { + errback(xhr.status, url); + } + } + + if (isFileProtocol && !options.fileAsync) { + if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { + callback(xhr.responseText); + } else { + errback(xhr.status, url); + } + } else if (async) { + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + handleResponse(xhr, callback, errback); + } + }; + } else { + handleResponse(xhr, callback, errback); + } +}; +BrowserImport.prototype.supports = function(filename, currentDirectory, options, environment) { + return true; +}; + +BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment) { + return new PromiseConstructor(function(fullfill, reject) { + if (currentDirectory && !this.isPathAbsolute(filename)) { + filename = currentDirectory + filename; + } + + options = options || {}; + + // sheet may be set to the stylesheet for the initial load or a collection of properties including + // some env variables for imports + var hrefParts = this.extractUrlParts(filename, window.location.href); + var href = hrefParts.url; + + if (options.useFileCache && fileCache[href]) { + try { + var lessText = fileCache[href]; + fullfill({ content: lessText, filename: href, webInfo: { lastModified: new Date() }}); + } catch (e) { + reject({filename: href, message: "Error loading file " + href + " error was " + e.message}); + } + return; + } + + this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) { + // per file cache + fileCache[href] = data; + + // Use remote copy (re-parse) + fullfill({ content: data, filename: href, webInfo: { lastModified: lastModified }}); + }, function doXHRError(status, url) { + reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", filename: href }); + }); + }.bind(this)); +}; + + +return new BrowserImport(); +}; diff --git a/lib/less-browser/environment.js b/lib/less-browser/environment.js index 5f65bd485..6adc7d66e 100644 --- a/lib/less-browser/environment.js +++ b/lib/less-browser/environment.js @@ -1,206 +1,5 @@ -/*global window, XMLHttpRequest */ - -module.exports = function(options, isFileProtocol, log, logLevel) { - -var fileCache = {}; - -//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load -// isFileProtocol is global - -function getXMLHttpRequest() { - if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { - return new XMLHttpRequest(); - } else { - try { - /*global ActiveXObject */ - return new ActiveXObject("Microsoft.XMLHTTP"); - } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); - return null; - } - } -} - -return { - // make generic but overriddable +module.exports = { warn: function warn(msg) { console.warn(msg); - }, - // make generic but overriddable - getPath: function getPath(filename) { - var j = filename.lastIndexOf('/'); - if (j < 0) { - j = filename.lastIndexOf('\\'); - } - if (j < 0) { - return ""; - } - return filename.slice(0, j + 1); - }, - // make generic but overriddable - isPathAbsolute: function isPathAbsolute(filename) { - return /^(?:[a-z-]+:|\/|\\)/i.test(filename); - }, - alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { - return true; - }, - supportsDataURI: function() { - return false; - }, - pathDiff: function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = this.extractUrlParts(url), - baseUrlParts = this.extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; - }, - join: function join(basePath, laterPath) { - if (!basePath) { - return laterPath; - } - return this.extractUrlParts(laterPath, basePath).path; - }, - // helper function, not part of API - extractUrlParts: function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; - }, - doXHR: function doXHR(url, type, callback, errback) { - - var xhr = getXMLHttpRequest(); - var async = isFileProtocol ? options.fileAsync : options.async; - - if (typeof(xhr.overrideMimeType) === 'function') { - xhr.overrideMimeType('text/css'); - } - log("XHR: Getting '" + url + "'", logLevel.debug); - xhr.open('GET', url, async); - xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); - xhr.send(null); - - function handleResponse(xhr, callback, errback) { - if (xhr.status >= 200 && xhr.status < 300) { - callback(xhr.responseText, - xhr.getResponseHeader("Last-Modified")); - } else if (typeof(errback) === 'function') { - errback(xhr.status, url); - } - } - - if (isFileProtocol && !options.fileAsync) { - if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { - callback(xhr.responseText); - } else { - errback(xhr.status, url); - } - } else if (async) { - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - handleResponse(xhr, callback, errback); - } - }; - } else { - handleResponse(xhr, callback, errback); - } - }, - loadFile: function loadFile(filename, currentDirectory, options, callback) { - if (currentDirectory && !this.isPathAbsolute(filename)) { - filename = currentDirectory + filename; - } - - options = options || {}; - - // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports - var hrefParts = this.extractUrlParts(filename, window.location.href); - var href = hrefParts.url; - - if (options.useFileCache && fileCache[href]) { - try { - var lessText = fileCache[href]; - callback(null, lessText, href, { lastModified: new Date() }); - } catch (e) { - callback(e, null, href); - } - return; - } - - this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) { - // per file cache - fileCache[href] = data; - - // Use remote copy (re-parse) - callback(null, data, href, { lastModified: lastModified }); - }, function doXHRError(status, url) { - callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href); - }); - } }; - -}; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 59d68bb8b..87368841d 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -29,9 +29,10 @@ function log(str, level) { var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), options = window.less || {}, - environment = require("./environment")(options, isFileProtocol, log, logLevel); + environment = require("./environment"), + browserImport = require("./browser-import")(options, isFileProtocol, log, logLevel); -window.less = less = require('../less')(environment); +window.less = less = require('../less')(environment, [browserImport]); less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || @@ -41,6 +42,10 @@ less.env = options.env || (location.hostname == '127.0.0.1' || isFileProtocol ? 'development' : 'production'); +if (!options.hasOwnProperty('disableDataURIs')) { + options.disableDataURIs = true; +} + // The amount of logging in the javascript console. // 3 - Debug, information and errors // 2 - Information and errors @@ -377,7 +382,12 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { instanceOptions.useFileCache = true; } - less.environment.loadFile(sheet.href, null, instanceOptions, function loadInitialFileCallback(e, data, path, webInfo) { + browserImport.loadFile(sheet.href, null, instanceOptions, environment) + .then(function loadInitialFileCallback(loadedFile) { + + var data = loadedFile.contents, + path = loadedFile.filename, + webInfo = loadedFile.webInfo; var newFileInfo = { currentDirectory: less.environment.getPath(path), @@ -408,18 +418,18 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { //TODO add tests around how this behaves when reloading removeError(path); - if (data) { - instanceOptions.rootFileInfo = newFileInfo; - less.render(data, instanceOptions) - .then(function(result) { - callback(null, result.css, data, sheet, webInfo, path); - }, - function(e) { - callback(e, null, null, sheet); - }); - } else { - callback(e, null, null, sheet, webInfo, path); - } + instanceOptions.rootFileInfo = newFileInfo; + less.render(data, instanceOptions) + .then(function(result) { + callback(null, result.css, data, sheet, webInfo, path); + }, + function(e) { + e.href = path; + callback(e); + }); + }, + function(e) { + callback(e); }); } @@ -435,7 +445,7 @@ function initRunningMode(){ if (less.watchMode) { loadStyleSheets(function (e, css, _, sheet, env) { if (e) { - error(e, sheet.href); + error(e, e.href || sheet.href); } else if (css) { css = postProcessCSS(css); createCSS(css, sheet, env.lastModified); @@ -498,7 +508,7 @@ less.refresh = function (reload, modifyVars) { loadStyleSheets(function (e, css, _, sheet, webInfo) { if (e) { - return error(e, sheet.href); + return error(e, e.href || sheet.href); } if (webInfo.local) { log("loading " + sheet.href + " from cache.", logLevel.info); diff --git a/lib/less-node/environment.js b/lib/less-node/environment.js index f4abb06fd..63b564b51 100644 --- a/lib/less-node/environment.js +++ b/lib/less-node/environment.js @@ -1,9 +1,3 @@ -var path = require('path'), - url = require('url'), - request, - fs = require('./fs'), - isUrlRe = /^(?:https?:)?\/\//i; - module.exports = { warn: function(msg) { console.warn(msg); @@ -11,211 +5,13 @@ module.exports = { encodeBase64: function encodeBase64(str) { return new Buffer(str).toString('base64'); }, - supportsDataURI: function() { - return true; - }, mimeLookup: function (filename) { return require('mime').lookup(filename); }, charsetLookup: function (mime) { return require('mime').charsets.lookup(mime); }, - readFileSync: function (filename) { - return require("fs").readFileSync(filename); - }, - getPath: function (filename) { - var j = filename.lastIndexOf('/'); - if (j < 0) { - j = filename.lastIndexOf('\\'); - } - if (j < 0) { - return ""; - } - return filename.slice(0, j + 1); - }, - isPathAbsolute: function(filename) { - return (/^(?:[a-z-]+:|\/|\\)/i).test(filename); - }, - getAbsolutePath: function getAbsolutePath(filename) { - return require('path').resolve(filename); - }, getSourceMapGenerator: function getSourceMapGenerator() { return require("source-map").SourceMapGenerator; - }, - alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() { - return false; - }, - join: function join(basePath, laterPath) { - if (!basePath) { - return laterPath; - } - return basePath + laterPath; - }, - pathDiff: function pathDiff(url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = this.extractUrlParts(url), - baseUrlParts = this.extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; - } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; - }, - // helper function, not part of API - extractUrlParts: function extractUrlParts(url, baseUrl) { - // urlParts[1] = protocol&hostname || / - // urlParts[2] = / if path relative to host base - // urlParts[3] = directories - // urlParts[4] = filename - // urlParts[5] = parameters - - var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, - urlParts = url.match(urlPartsRegex), - returner = {}, directories = [], i, baseUrlParts; - - if (!urlParts) { - throw new Error("Could not parse sheet href - '"+url+"'"); - } - - // Stylesheets in IE don't always return the full path - if (baseUrl && (!urlParts[1] || urlParts[2])) { - baseUrlParts = baseUrl.match(urlPartsRegex); - if (!baseUrlParts) { - throw new Error("Could not parse page url - '"+baseUrl+"'"); - } - urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; - if (!urlParts[2]) { - urlParts[3] = baseUrlParts[3] + urlParts[3]; - } - } - - if (urlParts[3]) { - directories = urlParts[3].replace(/\\/g, "/").split("/"); - - // extract out . before .. so .. doesn't absorb a non-directory - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".") { - directories.splice(i, 1); - i -= 1; - } - } - - for(i = 0; i < directories.length; i++) { - if (directories[i] === ".." && i > 0) { - directories.splice(i-1, 2); - i -= 2; - } - } - } - - returner.hostPart = urlParts[1]; - returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); - returner.fileUrl = returner.path + (urlParts[4] || ""); - returner.url = returner.fileUrl + (urlParts[5] || ""); - return returner; - }, - // consider if we really want this in the environment or whether all or some of this should be moved to (for node) built in import manager plugins? - loadFile: function(filename, currentDirectory, options, callback) { - var fullFilename, - data, - isUrl = isUrlRe.test( filename ); - - options = options || {}; - - if (isUrl || isUrlRe.test(currentDirectory)) { - if (request === undefined) { - try { request = require('request'); } - catch(e) { request = null; } - } - if (!request) { - callback({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" }); - return; - } - - var urlStr = isUrl ? filename : url.resolve(currentDirectory, filename), - urlObj = url.parse(urlStr); - - if (!urlObj.protocol) { - urlObj.protocol = "http"; - urlStr = urlObj.format(); - } - - request.get({uri: urlStr, strictSSL: !options.insecure }, function (error, res, body) { - if (error) { - callback({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" }); - } - if (res && res.statusCode === 404) { - callback({ type: 'File', message: "resource '" + urlStr + "' was not found\n" }); - return; - } - if (!body) { - this.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); - } - fullFilename = urlStr; - callback(null, body, fullFilename); - }); - } else { - - var paths = [currentDirectory]; - if (options.paths) paths.push.apply(paths, options.paths); - if (paths.indexOf('.') === -1) paths.push('.'); - - if (options.syncImport) { - for (var i = 0; i < paths.length; i++) { - try { - fullFilename = path.join(paths[i], filename); - fs.statSync(fullFilename); - break; - } catch (e) { - fullFilename = null; - } - } - - if (!fullFilename) { - callback({ type: 'File', message: "'" + filename + "' wasn't found" }); - return; - } - - data = fs.readFileSync(fullFilename, 'utf-8'); - callback(null, data, fullFilename); - } else { - (function tryPathIndex(i) { - if (i < paths.length) { - fullFilename = path.join(paths[i], filename); - fs.stat(fullFilename, function (err) { - if (err) { - tryPathIndex(i + 1); - } else { - fs.readFile(fullFilename, 'utf-8', function(e, data) { - if (e) { callback(e); } - - // do processing in the next tick to allow - // file handling to dispose - process.nextTick(function() { - callback(null, data, fullFilename); - }); - }); - } - }); - } else { - callback({ type: 'File', message: "'" + filename + "' wasn't found" }); - } - }(0)); - } - } } }; diff --git a/lib/less-node/file-import.js b/lib/less-node/file-import.js new file mode 100644 index 000000000..84734b71c --- /dev/null +++ b/lib/less-node/file-import.js @@ -0,0 +1,79 @@ +var path = require('path'), + fs = require('./fs'), + PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, + AbstractFileManager = require("../less/environment/abstract-file-manager.js"); + +var FileImport = function() { +}; + +FileImport.prototype = new AbstractFileManager(); + +FileImport.prototype.supports = function(filename, currentDirectory, options, environment) { + return true; +}; +FileImport.prototype.supportsSync = function(filename, currentDirectory, options, environment) { + return true; +}; + +FileImport.prototype.loadFile = function(filename, currentDirectory, options, environment) { + return new PromiseConstructor(function(fullfill, reject) { + var fullFilename, + data; + + options = options || {}; + + var paths = [currentDirectory]; + if (options.paths) paths.push.apply(paths, options.paths); + if (paths.indexOf('.') === -1) paths.push('.'); + + if (options.syncImport) { + for (var i = 0; i < paths.length; i++) { + try { + fullFilename = path.join(paths[i], filename); + fs.statSync(fullFilename); + break; + } catch (e) { + fullFilename = null; + } + } + + if (!fullFilename) { + reject({ type: 'File', message: "'" + filename + "' wasn't found" }); + return; + } + + data = fs.readFileSync(fullFilename, 'utf-8'); + fullfill({ contents: data, filename: fullFilename}); + } else { + (function tryPathIndex(i) { + if (i < paths.length) { + fullFilename = path.join(paths[i], filename); + fs.stat(fullFilename, function (err) { + if (err) { + tryPathIndex(i + 1); + } else { + fs.readFile(fullFilename, 'utf-8', function(e, data) { + if (e) { reject(e); return; } + + // do processing in the next tick to allow + // file handling to dispose + process.nextTick(function() { + fullfill({ contents: data, filename: fullFilename}); + }); + }); + } + }); + } else { + reject({ type: 'File', message: "'" + filename + "' wasn't found" }); + } + }(0)); + } + }); +}; + +FileImport.prototype.loadFileSync = function(filename, currentDirectory, options, environment) { + filename = path.join(currentDirectory, filename); + return { contents: fs.readFileSync(filename), filename: filename }; +}; + +module.exports = new FileImport(); diff --git a/lib/less-node/index.js b/lib/less-node/index.js index 63f2ded5e..83926908c 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -1,6 +1,8 @@ var environment = require("./environment"), + fileImport = require("./file-import.js"), + urlImport = require("./url-import.js"), createFromEnvironment = require("../less"), - less = createFromEnvironment(environment), + less = createFromEnvironment(environment, [fileImport, urlImport]), lesscHelper = require('./lessc-helper'); // allow people to create less with their own environment @@ -8,7 +10,8 @@ less.createFromEnvironment = createFromEnvironment; less.lesscHelper = lesscHelper; less.PluginManager = require("./node-plugin-manager"); less.fs = require("./fs"); -less.environment = environment; +less.fileImport = require("./file-import.js"); +less.urlImport = require("./url-import.js"); less.formatError = function(ctx, options) { options = options || {}; diff --git a/lib/less-node/url-import.js b/lib/less-node/url-import.js new file mode 100644 index 000000000..7d5aa46b9 --- /dev/null +++ b/lib/less-node/url-import.js @@ -0,0 +1,52 @@ +var isUrlRe = /^(?:https?:)?\/\//i, + url = require('url'), + request, + PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, + AbstractFileManager = require("../less/environment/abstract-file-manager.js"); + +var UrlImport = function() { +}; + +UrlImport.prototype = new AbstractFileManager(); + +UrlImport.prototype.supports = function(filename, currentDirectory, options, environment) { + return isUrlRe.test( filename ) || isUrlRe.test(currentDirectory); +}; + +UrlImport.prototype.loadFile = function(filename, currentDirectory, options, environment) { + return new PromiseConstructor(function(fullfill, reject) { + if (request === undefined) { + try { request = require('request'); } + catch(e) { request = null; } + } + if (!request) { + reject({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" }); + return; + } + + var urlStr = isUrlRe.test( filename ) ? filename : url.resolve(currentDirectory, filename), + urlObj = url.parse(urlStr); + + if (!urlObj.protocol) { + urlObj.protocol = "http"; + urlStr = urlObj.format(); + } + + request.get({uri: urlStr, strictSSL: !options.insecure }, function (error, res, body) { + if (error) { + reject({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" }); + return; + } + if (res && res.statusCode === 404) { + reject({ type: 'File', message: "resource '" + urlStr + "' was not found\n" }); + return; + } + if (!body) { + environment.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); + } + fullfill({ contents: body, filename: urlStr }); + }); + }); +}; + +module.exports = new UrlImport(); diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 467cfe6c5..b486535c3 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -49,7 +49,8 @@ var evalCopyProperties = [ 'sourceMap', // whether to output a source map 'importMultiple', // whether we are currently importing multiple copies 'urlArgs', // whether to add args into url tokens - 'javascriptEnabled'// option - whether JavaScript is enabled. if undefined, defaults to true + 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true + 'disableDataURIs' // option - disable data URIs (they will be converted to URL()) ]; contexts.evalEnv = function(options, frames) { diff --git a/lib/less/environment/abstract-file-manager.js b/lib/less/environment/abstract-file-manager.js new file mode 100644 index 000000000..1ca31b201 --- /dev/null +++ b/lib/less/environment/abstract-file-manager.js @@ -0,0 +1,111 @@ +var abstractFileManager = function() { +}; + +abstractFileManager.prototype.getPath = function (filename) { + var j = filename.lastIndexOf('/'); + if (j < 0) { + j = filename.lastIndexOf('\\'); + } + if (j < 0) { + return ""; + } + return filename.slice(0, j + 1); +}; + +abstractFileManager.prototype.supportsSync = function() { + return false; +}; + +abstractFileManager.prototype.alwaysMakePathsAbsolute = function() { + return false; +}; + +abstractFileManager.prototype.isPathAbsolute = function(filename) { + return (/^(?:[a-z-]+:|\/|\\)/i).test(filename); +}; + +abstractFileManager.prototype.join = function(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return basePath + laterPath; +}; +abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) { + // diff between two paths to create a relative path + + var urlParts = this.extractUrlParts(url), + baseUrlParts = this.extractUrlParts(baseUrl), + i, max, urlDirectories, baseUrlDirectories, diff = ""; + if (urlParts.hostPart !== baseUrlParts.hostPart) { + return ""; + } + max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); + for(i = 0; i < max; i++) { + if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } + } + baseUrlDirectories = baseUrlParts.directories.slice(i); + urlDirectories = urlParts.directories.slice(i); + for(i = 0; i < baseUrlDirectories.length-1; i++) { + diff += "../"; + } + for(i = 0; i < urlDirectories.length-1; i++) { + diff += urlDirectories[i] + "/"; + } + return diff; +}; +// helper function, not part of API +abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) { + // urlParts[1] = protocol&hostname || / + // urlParts[2] = / if path relative to host base + // urlParts[3] = directories + // urlParts[4] = filename + // urlParts[5] = parameters + + var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, + urlParts = url.match(urlPartsRegex), + returner = {}, directories = [], i, baseUrlParts; + + if (!urlParts) { + throw new Error("Could not parse sheet href - '" + url + "'"); + } + + // Stylesheets in IE don't always return the full path + if (!urlParts[1] || urlParts[2]) { + baseUrlParts = baseUrl.match(urlPartsRegex); + if (!baseUrlParts) { + throw new Error("Could not parse page url - '"+baseUrl+"'"); + } + urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; + if (!urlParts[2]) { + urlParts[3] = baseUrlParts[3] + urlParts[3]; + } + } + + if (urlParts[3]) { + directories = urlParts[3].replace(/\\/g, "/").split("/"); + + // extract out . before .. so .. doesn't absorb a non-directory + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".") { + directories.splice(i, 1); + i -= 1; + } + } + + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".." && i > 0) { + directories.splice(i-1, 2); + i -= 2; + } + } + } + + returner.hostPart = urlParts[1]; + returner.directories = directories; + returner.path = urlParts[1] + directories.join("/"); + returner.fileUrl = returner.path + (urlParts[4] || ""); + returner.url = returner.fileUrl + (urlParts[5] || ""); + return returner; +}; + +module.exports = abstractFileManager; diff --git a/lib/less/environment/api.js b/lib/less/environment/api.js index 90a71fc7a..bd72a50f9 100644 --- a/lib/less/environment/api.js +++ b/lib/less/environment/api.js @@ -5,6 +5,29 @@ module.exports = { */ warn: function(msg) { }, + /** + * Converts a string to a base 64 string + * @param str + */ + encodeBase64: function(str) { + }, + /** + * Lookup the mime-type of a filename + * @param filename + */ + mimeLookup: function (filename) { + }, + /** + * Look up the charset of a mime type + * @param mime + */ + charsetLookup: function (mime) { + }, + /** + * + */ + getSourceMapGenerator: function getSourceMapGenerator() { + }, /** * gets the path from the filename, e.g. "http://wwe.files.com/ha/ha.less" would return * "http://wwe.files.com/ha/" diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js new file mode 100644 index 000000000..454bcaa20 --- /dev/null +++ b/lib/less/environment/environment.js @@ -0,0 +1,36 @@ +var environment = function(externalEnvironment, fileManagers) { + this.fileManagers = fileManagers || []; + var functions = ["warn", "encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"], + emptyFunc = function() {}; + + for(var i = 0; i < functions.length; i++) { + var propName = functions[i], + environmentFunc = externalEnvironment[propName]; + if (environmentFunc) { + this[propName] = environmentFunc.bind(externalEnvironment); + } else { + this[propName] = emptyFunc; + this.warn("missing function in environment - " + propName); + } + } +}; + +environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { + for(var i = this.fileManagers.length - 1; i >= 0 ; i--) { + var fileManager = this.fileManagers[i]; + if (fileManager[isSync ? "supports" : "supportsSync"](filename, currentDirectory, options, environment)) { + return fileManager; + } + } + return null; +}; + +environment.prototype.addFileManager = function (fileManager) { + this.fileManagers.push(fileManager); +}; + +environment.prototype.clearFileManagers = function () { + this.fileManagers = []; +}; + +module.exports = environment; diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index 8a4ee5e06..76d44c134 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -1,17 +1,22 @@ module.exports = function(environment) { var Anonymous = require("../tree/anonymous"), URL = require("../tree/url"), - functionRegistry = require("./function-registry"); + functionRegistry = require("./function-registry"), + fallback = function(functionContext, node) { + return new URL(node, functionContext.index, functionContext.currentFileInfo).eval(functionContext.env); + }; functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { - if (!environment.supportsDataURI()) { - return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env); - } - var mimetype = mimetypeNode.value; var filePath = (filePathNode && filePathNode.value); + var fileManager = environment.getFileManager(filePath, this.env.currentFileInfo, this.env, environment, true); + + if (!fileManager) { + return fallback(this, filePathNode || mimetypeNode); + } + var useBase64 = false; if (arguments.length < 2) { @@ -25,13 +30,8 @@ module.exports = function(environment) { filePath = filePath.slice(0, fragmentStart); } - if (this.env.isPathRelative(filePath)) { - if (this.currentFileInfo.relativeUrls) { - filePath = environment.join(this.currentFileInfo.currentDirectory, filePath); - } else { - filePath = environment.join(this.currentFileInfo.entryPath, filePath); - } - } + var currentDirectory = this.currentFileInfo.relativeUrls ? + this.currentFileInfo.currentDirectory : this.currentFileInfo.entryPath; // detect the mimetype if not given if (arguments.length < 2) { @@ -47,7 +47,12 @@ module.exports = function(environment) { useBase64 = /;base64$/.test(mimetype); } - var buf = environment.readFileSync(filePath); + var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.env, environment); + if (!fileSync.contents) { + environment.warn("Skipped data-uri embedding because file not found"); + return fallback(this, filePathNode || mimetypeNode); + } + var buf = fileSync.contents; // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded // and the --ieCompat flag is enabled, return a normal url() instead. @@ -56,11 +61,9 @@ module.exports = function(environment) { if (fileSizeInKB >= DATA_URI_MAX_KB) { if (this.env.ieCompat !== false) { - if (!this.env.silent) { - console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - } + environment.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); - return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env); + return fallback(this, filePathNode || mimetypeNode); } } diff --git a/lib/less/imports.js b/lib/less/imports.js index e426c6dd3..a8299c544 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -24,11 +24,6 @@ module.exports = function(environment) { this.queue = []; // Files which haven't been imported yet this.files = []; // Holds the imported parse trees. }; - ImportManager.prototype.getAbsolutePath = function(filename) { - // proxy needed for "DebugInfo" - // I hope one day we can remove this function - return environment.getAbsolutePath(filename); - }; ImportManager.prototype.push = function (path, currentFileInfo, importOptions, callback) { var parserImports = this; this.queue.push(path); @@ -52,46 +47,55 @@ module.exports = function(environment) { rootFilename: currentFileInfo.rootFilename }; - environment.loadFile(path, currentFileInfo.currentDirectory, this.env, function loadFileCallback(e, contents, resolvedFilename) { - if (e) { - fileParsedFunc(e); - return; - } - - // Pass on an updated rootpath if path of imported file is relative and file - // is in a (sub|sup) directory - // - // Examples: - // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', - // then rootpath should become 'less/module/nav/' - // - If path of imported file is '../mixins.less' and rootpath is 'less/', - // then rootpath should become 'less/../' - newFileInfo.currentDirectory = environment.getPath(resolvedFilename); - if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = environment.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); - if (!environment.isPathAbsolute(newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) { - newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath); + var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.env, environment); + + if (!fileManager) { + fileParsedFunc({ message: "Could not find a file-manager for " + path }); + return; + } + + fileManager.loadFile(path, currentFileInfo.currentDirectory, this.env, environment) + .then(function loadFileCallback(loadedFile) { + var resolvedFilename = loadedFile.filename, + contents = loadedFile.contents; + + // Pass on an updated rootpath if path of imported file is relative and file + // is in a (sub|sup) directory + // + // Examples: + // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', + // then rootpath should become 'less/module/nav/' + // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // then rootpath should become 'less/../' + newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename); + if(newFileInfo.relativeUrls) { + newFileInfo.rootpath = fileManager.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath); + } + } + newFileInfo.filename = resolvedFilename; + + var newEnv = new contexts.parseEnv(parserImports.env); + + newEnv.processImports = false; + parserImports.contents[resolvedFilename] = contents; + + if (currentFileInfo.reference || importOptions.reference) { + newFileInfo.reference = true; + } + + if (importOptions.inline) { + fileParsedFunc(null, contents, resolvedFilename); + } else { + new Parser(newEnv, parserImports, newFileInfo).parse(contents, function (e, root) { + fileParsedFunc(e, root, resolvedFilename); + }); } - } - newFileInfo.filename = resolvedFilename; - - var newEnv = new contexts.parseEnv(parserImports.env); - - newEnv.processImports = false; - parserImports.contents[resolvedFilename] = contents; - - if (currentFileInfo.reference || importOptions.reference) { - newFileInfo.reference = true; - } - - if (importOptions.inline) { - fileParsedFunc(null, contents, resolvedFilename); - } else { - new Parser(newEnv, parserImports, newFileInfo).parse(contents, function (e, root) { - fileParsedFunc(e, root, resolvedFilename); - }); - } - }); + }, + function(error) { + fileParsedFunc(error); + }); }; return ImportManager; }; diff --git a/lib/less/index.js b/lib/less/index.js index 5b1f5cefc..e520b2c0f 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,17 +1,19 @@ -module.exports = function(environment) { - var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager; +module.exports = function(environment, fileManagers) { + var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment; var less = { version: [2, 0, 0], data: require('./data'), tree: require('./tree'), + Environment: (Environment = require("./environment/environment")), + AbstractFileManager: require("./environment/abstract-file-manager"), + environment: (environment = new Environment(environment, fileManagers)), visitors: require('./visitors'), Parser: require('./parser/parser'), functions: require('./functions')(environment), contexts: require("./contexts"), - environment: environment, - SourceMapOutput: (SourceMapOutput = require('./source-map-output.js')(environment)), - SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder.js')(SourceMapOutput)), + SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)), + SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput)), ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)), ImportManager: (ImportManager = require('./imports')(environment)), render: require("./render")(environment, ParseTree, ImportManager), diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 85792d3e0..43a6ba1e9 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -73,7 +73,6 @@ var Parser = function Parser(env, imports, fileInfo) { function getDebugInfo(index) { var filename = fileInfo.filename; - filename = imports.getAbsolutePath(filename); return { lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, diff --git a/lib/less/source-map-builder.js b/lib/less/source-map-builder.js index 3cd382f45..f212d113b 100644 --- a/lib/less/source-map-builder.js +++ b/lib/less/source-map-builder.js @@ -23,7 +23,9 @@ module.exports = function (SourceMapOutput) { var css = sourceMapOutput.toCSS(options); this.sourceMap = sourceMapOutput.sourceMap; this.sourceMapURL = sourceMapOutput.sourceMapURL; - this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename); + if (this.options.sourceMapInputFilename) { + this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename); + } return css; }; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index e0ac6d81d..917199054 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -111,7 +111,6 @@ Import.prototype.eval = function (env) { } if (this.options.inline) { - //todo needs to reference css file not import var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); return this.features ? new(Media)([contents], this.features.value) : [contents]; } else if (this.css) { From 70bf802b2c91888617af4b4570e8833dc8a4bf25 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 4 Oct 2014 18:04:50 +0100 Subject: [PATCH 108/161] Fix the main browser tests --- lib/less-browser/browser-import.js | 7 +++---- lib/less-browser/index.js | 2 +- lib/less/environment/environment.js | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js index 5a5ec3fe4..d06169b11 100644 --- a/lib/less-browser/browser-import.js +++ b/lib/less-browser/browser-import.js @@ -22,7 +22,7 @@ function getXMLHttpRequest() { return null; } } -}; +} var BrowserImport = function() { }; @@ -113,7 +113,7 @@ BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, if (options.useFileCache && fileCache[href]) { try { var lessText = fileCache[href]; - fullfill({ content: lessText, filename: href, webInfo: { lastModified: new Date() }}); + fullfill({ contents: lessText, filename: href, webInfo: { lastModified: new Date() }}); } catch (e) { reject({filename: href, message: "Error loading file " + href + " error was " + e.message}); } @@ -125,13 +125,12 @@ BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, fileCache[href] = data; // Use remote copy (re-parse) - fullfill({ content: data, filename: href, webInfo: { lastModified: lastModified }}); + fullfill({ contents: data, filename: href, webInfo: { lastModified: lastModified }}); }, function doXHRError(status, url) { reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", filename: href }); }); }.bind(this)); }; - return new BrowserImport(); }; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 87368841d..375f94f5d 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -390,7 +390,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { webInfo = loadedFile.webInfo; var newFileInfo = { - currentDirectory: less.environment.getPath(path), + currentDirectory: browserImport.getPath(path), filename: path, rootFilename: path, relativeUrls: instanceOptions.relativeUrls}; diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js index 454bcaa20..4508cf6ac 100644 --- a/lib/less/environment/environment.js +++ b/lib/less/environment/environment.js @@ -18,7 +18,7 @@ var environment = function(externalEnvironment, fileManagers) { environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { for(var i = this.fileManagers.length - 1; i >= 0 ; i--) { var fileManager = this.fileManagers[i]; - if (fileManager[isSync ? "supports" : "supportsSync"](filename, currentDirectory, options, environment)) { + if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) { return fileManager; } } From e040eff3e964ae14ed7a1e3716fb022a66466ee6 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 4 Oct 2014 18:14:31 +0100 Subject: [PATCH 109/161] Add back in test core js files --- .npmignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.npmignore b/.npmignore index 9e9b12e44..122bb69e2 100644 --- a/.npmignore +++ b/.npmignore @@ -2,4 +2,7 @@ build/ benchmark/ test/ +# re-include test files as they can be useful for plugins that do testing +!test/*.js +tmp/ gradle/ \ No newline at end of file From 0c168e1c8d7fc5ff2e262ad24436d1f2bd65e63e Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 4 Oct 2014 18:35:56 +0100 Subject: [PATCH 110/161] Fix error filename --- lib/less-browser/browser-import.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js index d06169b11..997f69265 100644 --- a/lib/less-browser/browser-import.js +++ b/lib/less-browser/browser-import.js @@ -127,7 +127,7 @@ BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, // Use remote copy (re-parse) fullfill({ contents: data, filename: href, webInfo: { lastModified: lastModified }}); }, function doXHRError(status, url) { - reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", filename: href }); + reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href }); }); }.bind(this)); }; From 27494c280e3c52e50af7a41652e1766ddab3ece2 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 4 Oct 2014 18:49:12 +0100 Subject: [PATCH 111/161] Fix svg function in browser --- lib/less/environment/environment.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js index 4508cf6ac..aeb8805cd 100644 --- a/lib/less/environment/environment.js +++ b/lib/less/environment/environment.js @@ -1,7 +1,6 @@ var environment = function(externalEnvironment, fileManagers) { this.fileManagers = fileManagers || []; - var functions = ["warn", "encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"], - emptyFunc = function() {}; + var functions = ["warn", "encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"]; for(var i = 0; i < functions.length; i++) { var propName = functions[i], @@ -9,12 +8,14 @@ var environment = function(externalEnvironment, fileManagers) { if (environmentFunc) { this[propName] = environmentFunc.bind(externalEnvironment); } else { - this[propName] = emptyFunc; this.warn("missing function in environment - " + propName); } } }; +environment.prototype.warn = function (msg) { +}; + environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { for(var i = this.fileManagers.length - 1; i >= 0 ; i--) { var fileManager = this.fileManagers[i]; From ee9eefd94f47e037231392d5ee66edd0f77af8de Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 5 Oct 2014 09:30:00 +0100 Subject: [PATCH 112/161] Fix some more browser tests --- Gruntfile.js | 2 +- lib/less-browser/browser-import.js | 25 ++++--------------------- lib/less/imports.js | 2 +- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 126cb2366..37275a716 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -37,7 +37,7 @@ module.exports = function (grunt) { command: 'node benchmark/less-benchmark.js' }, "browsertest-server": { - command: 'node node_modules/http-server/bin/http-server . -p 8088' + command: 'node node_modules/http-server/bin/http-server . -p 8081' }, "sourcemap-test": { command: [ diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js index 997f69265..d2e49aefb 100644 --- a/lib/less-browser/browser-import.js +++ b/lib/less-browser/browser-import.js @@ -32,28 +32,11 @@ BrowserImport.prototype = new AbstractFileManager(); BrowserImport.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() { return true; }; -BrowserImport.prototype.join = function (url, baseUrl) { - // diff between two paths to create a relative path - - var urlParts = this.extractUrlParts(url), - baseUrlParts = this.extractUrlParts(baseUrl), - i, max, urlDirectories, baseUrlDirectories, diff = ""; - if (urlParts.hostPart !== baseUrlParts.hostPart) { - return ""; +BrowserImport.prototype.join = function join(basePath, laterPath) { + if (!basePath) { + return laterPath; } - max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); - for(i = 0; i < max; i++) { - if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } - } - baseUrlDirectories = baseUrlParts.directories.slice(i); - urlDirectories = urlParts.directories.slice(i); - for(i = 0; i < baseUrlDirectories.length-1; i++) { - diff += "../"; - } - for(i = 0; i < urlDirectories.length-1; i++) { - diff += urlDirectories[i] + "/"; - } - return diff; + return this.extractUrlParts(laterPath, basePath).path; }; BrowserImport.prototype.doXHR = function doXHR(url, type, callback, errback) { diff --git a/lib/less/imports.js b/lib/less/imports.js index a8299c544..00bedc3b8 100644 --- a/lib/less/imports.js +++ b/lib/less/imports.js @@ -69,7 +69,7 @@ module.exports = function(environment) { // then rootpath should become 'less/../' newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename); if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = fileManager.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + newFileInfo.rootpath = fileManager.join((parserImports.env.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) { newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath); } From 3af09233b1b64fad6d45e9ca255e54476f3cadac Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 5 Oct 2014 10:42:38 +0100 Subject: [PATCH 113/161] Fix final weird error - in phantom only some console.log calls are going missing? --- lib/less-browser/index.js | 4 +++- lib/less/environment/environment.js | 8 +++++--- test/browser/common.js | 19 ++++++++++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 375f94f5d..54e038cdf 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -14,7 +14,9 @@ var less; function log(str, level) { if (typeof(console) !== 'undefined' && less.logLevel >= level) { - console.log('less: ' + str); + //TODO no idea why console.log has stopped working in phantom + //move to a proper logger and registering a listener + console.warn('less: ' + str); } } diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js index aeb8805cd..84b4160f4 100644 --- a/lib/less/environment/environment.js +++ b/lib/less/environment/environment.js @@ -1,14 +1,16 @@ var environment = function(externalEnvironment, fileManagers) { this.fileManagers = fileManagers || []; - var functions = ["warn", "encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"]; + var optionalFunctions = ["encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"], + requiredFunctions = ["warn"], + functions = requiredFunctions.concat(optionalFunctions); for(var i = 0; i < functions.length; i++) { var propName = functions[i], environmentFunc = externalEnvironment[propName]; if (environmentFunc) { this[propName] = environmentFunc.bind(externalEnvironment); - } else { - this.warn("missing function in environment - " + propName); + } else if (i < requiredFunctions.length) { + this.warn("missing required function in environment - " + propName); } } }; diff --git a/test/browser/common.js b/test/browser/common.js index 21ec8a383..09e2e5455 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -10,11 +10,20 @@ // } // }; -var logMessages = [], - realConsoleLog = console.log; -console.log = function(msg) { - logMessages.push(msg); - realConsoleLog.call(console, msg); +var oldconsole = window.console; +var logMessages = []; +window.console = { + warn: function(msg) { + oldconsole.warn(msg); + logMessages.push(msg); + }, + log: function(msg) { + oldconsole.log(msg); + logMessages.push(msg); + }, + error: function(msg) { + oldconsole.error(msg); + } }; var testLessEqualsInDocument = function() { From 414df4d4829b391877d32cf9f0a3c67ae8fc8acc Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 5 Oct 2014 10:53:52 +0100 Subject: [PATCH 114/161] rename imports to class name --- lib/less/{imports.js => import-manager.js} | 0 lib/less/index.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/less/{imports.js => import-manager.js} (100%) diff --git a/lib/less/imports.js b/lib/less/import-manager.js similarity index 100% rename from lib/less/imports.js rename to lib/less/import-manager.js diff --git a/lib/less/index.js b/lib/less/index.js index e520b2c0f..b2be18b53 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -15,7 +15,7 @@ module.exports = function(environment, fileManagers) { SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)), SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput)), ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)), - ImportManager: (ImportManager = require('./imports')(environment)), + ImportManager: (ImportManager = require('./import-manager')(environment)), render: require("./render")(environment, ParseTree, ImportManager), LessError: require('./less-error'), transformTree: require('./transform-tree'), From dedf0eee38e9095dff7d71a98a831d1e234869f3 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 5 Oct 2014 18:50:34 +0100 Subject: [PATCH 115/161] rename env to context to avoid confusion with environment --- lib/less-browser/browser-import.js | 2 +- lib/less-browser/index.js | 4 +- lib/less/contexts.js | 21 +++--- lib/less/environment/api.js | 2 +- lib/less/functions/data-uri.js | 10 +-- lib/less/functions/function-caller.js | 4 +- lib/less/functions/number.js | 2 +- lib/less/import-manager.js | 32 ++++----- lib/less/parser/parser.js | 20 +++--- lib/less/render.js | 10 +-- lib/less/source-map-output.js | 4 +- lib/less/transform-tree.js | 2 +- lib/less/tree/alpha.js | 8 +-- lib/less/tree/anonymous.js | 2 +- lib/less/tree/assignment.js | 8 +-- lib/less/tree/attribute.js | 16 ++--- lib/less/tree/call.js | 10 +-- lib/less/tree/color.js | 14 ++-- lib/less/tree/combinator.js | 4 +- lib/less/tree/comment.js | 8 +-- lib/less/tree/condition.js | 4 +- lib/less/tree/debug-info.js | 6 +- lib/less/tree/detached-ruleset.js | 8 +-- lib/less/tree/dimension.js | 20 +++--- lib/less/tree/directive.js | 30 ++++----- lib/less/tree/element.js | 22 +++---- lib/less/tree/expression.js | 16 ++--- lib/less/tree/extend.js | 6 +- lib/less/tree/import.js | 26 ++++---- lib/less/tree/javascript.js | 4 +- lib/less/tree/js-eval-node.js | 16 ++--- lib/less/tree/keyword.js | 2 +- lib/less/tree/media.js | 52 +++++++-------- lib/less/tree/mixin-call.js | 22 +++---- lib/less/tree/mixin-definition.js | 42 ++++++------ lib/less/tree/negative.js | 12 ++-- lib/less/tree/node.js | 12 ++-- lib/less/tree/operation.js | 16 ++--- lib/less/tree/paren.js | 8 +-- lib/less/tree/quoted.js | 8 +-- lib/less/tree/rule.js | 24 +++---- lib/less/tree/ruleset-call.js | 6 +- lib/less/tree/ruleset.js | 94 +++++++++++++-------------- lib/less/tree/selector.js | 18 ++--- lib/less/tree/unit.js | 4 +- lib/less/tree/url.js | 16 ++--- lib/less/tree/value.js | 12 ++-- lib/less/tree/variable.js | 8 +-- lib/less/visitors/import-visitor.js | 30 ++++----- lib/less/visitors/to-css-visitor.js | 12 ++-- 50 files changed, 368 insertions(+), 371 deletions(-) diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js index d2e49aefb..8e4b4a0c8 100644 --- a/lib/less-browser/browser-import.js +++ b/lib/less-browser/browser-import.js @@ -89,7 +89,7 @@ BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, options = options || {}; // sheet may be set to the stylesheet for the initial load or a collection of properties including - // some env variables for imports + // some context variables for imports var hrefParts = this.extractUrlParts(filename, window.location.href); var href = hrefParts.url; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 54e038cdf..2d48326d7 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -445,12 +445,12 @@ function initRunningMode(){ if (less.env === 'development') { less.watchTimer = setInterval(function () { if (less.watchMode) { - loadStyleSheets(function (e, css, _, sheet, env) { + loadStyleSheets(function (e, css, _, sheet, context) { if (e) { error(e, e.href || sheet.href); } else if (css) { css = postProcessCSS(css); - createCSS(css, sheet, env.lastModified); + createCSS(css, sheet, context.lastModified); } }); } diff --git a/lib/less/contexts.js b/lib/less/contexts.js index b486535c3..84da3e60d 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -12,7 +12,7 @@ var copyFromOriginal = function copyFromOriginal(original, destination, properti }; /* - parseEnv is used whilst parsing + parse is used whilst parsing */ var parseCopyProperties = [ // options @@ -33,7 +33,7 @@ var parseCopyProperties = [ 'reference' // Used to indicate that the contents are imported by reference ]; -contexts.parseEnv = function(options) { +contexts.Parse = function(options) { copyFromOriginal(options, this, parseCopyProperties); if (typeof this.paths === "string") { this.paths = [this.paths]; } @@ -53,32 +53,32 @@ var evalCopyProperties = [ 'disableDataURIs' // option - disable data URIs (they will be converted to URL()) ]; -contexts.evalEnv = function(options, frames) { +contexts.Eval = function(options, frames) { copyFromOriginal(options, this, evalCopyProperties); this.frames = frames || []; }; -contexts.evalEnv.prototype.inParenthesis = function () { +contexts.Eval.prototype.inParenthesis = function () { if (!this.parensStack) { this.parensStack = []; } this.parensStack.push(true); }; -contexts.evalEnv.prototype.outOfParenthesis = function () { +contexts.Eval.prototype.outOfParenthesis = function () { this.parensStack.pop(); }; -contexts.evalEnv.prototype.isMathOn = function () { +contexts.Eval.prototype.isMathOn = function () { return this.strictMath ? (this.parensStack && this.parensStack.length) : true; }; -contexts.evalEnv.prototype.isPathRelative = function (path) { +contexts.Eval.prototype.isPathRelative = function (path) { return !/^(?:[a-z-]+:|\/)/.test(path); }; -contexts.evalEnv.prototype.normalizePath = function( path ) { +contexts.Eval.prototype.normalizePath = function( path ) { var segments = path.split("/").reverse(), segment; @@ -105,8 +105,5 @@ contexts.evalEnv.prototype.normalizePath = function( path ) { return path.join("/"); }; -//todo - do the same for the toCSS env -//tree.toCSSEnv = function (options) { -//}; - +//todo - do the same for the toCSS ? diff --git a/lib/less/environment/api.js b/lib/less/environment/api.js index bd72a50f9..2ba3f6d1f 100644 --- a/lib/less/environment/api.js +++ b/lib/less/environment/api.js @@ -48,7 +48,7 @@ module.exports = { * Loads a file for an import aynscronously (or syncronously) * @param {String} filename - the filename * @param {String} currentDirectory - the current directory we are in - * @param {Object} options - the environment or options object + * @param {Object} options - the context/options object * @param {Function} callback - a function to callback when finished, * taking the format callback(error, contents, fullfilename, reserved) * where error is { type: {string}, message: {string} }, contents is {string} and fullfilename is {string} diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index 76d44c134..205dfb465 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -2,8 +2,8 @@ module.exports = function(environment) { var Anonymous = require("../tree/anonymous"), URL = require("../tree/url"), functionRegistry = require("./function-registry"), - fallback = function(functionContext, node) { - return new URL(node, functionContext.index, functionContext.currentFileInfo).eval(functionContext.env); + fallback = function(functionThis, node) { + return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context); }; functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { @@ -11,7 +11,7 @@ module.exports = function(environment) { var mimetype = mimetypeNode.value; var filePath = (filePathNode && filePathNode.value); - var fileManager = environment.getFileManager(filePath, this.env.currentFileInfo, this.env, environment, true); + var fileManager = environment.getFileManager(filePath, this.context.currentFileInfo, this.context, environment, true); if (!fileManager) { return fallback(this, filePathNode || mimetypeNode); @@ -47,7 +47,7 @@ module.exports = function(environment) { useBase64 = /;base64$/.test(mimetype); } - var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.env, environment); + var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment); if (!fileSync.contents) { environment.warn("Skipped data-uri embedding because file not found"); return fallback(this, filePathNode || mimetypeNode); @@ -60,7 +60,7 @@ module.exports = function(environment) { fileSizeInKB = parseInt((buf.length / 1024), 10); if (fileSizeInKB >= DATA_URI_MAX_KB) { - if (this.env.ieCompat !== false) { + if (this.context.ieCompat !== false) { environment.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); return fallback(this, filePathNode || mimetypeNode); diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js index 1e4f7965b..22e1e300d 100644 --- a/lib/less/functions/function-caller.js +++ b/lib/less/functions/function-caller.js @@ -1,10 +1,10 @@ var functionRegistry = require("./function-registry"); -var functionCaller = function(name, env, index, currentFileInfo) { +var functionCaller = function(name, context, index, currentFileInfo) { this.name = name.toLowerCase(); this.function = functionRegistry.get(this.name); this.index = index; - this.env = env; + this.context = context; this.currentFileInfo = currentFileInfo; }; functionCaller.prototype.isValid = function() { diff --git a/lib/less/functions/number.js b/lib/less/functions/number.js index 38d12f581..c4fb3ae22 100644 --- a/lib/less/functions/number.js +++ b/lib/less/functions/number.js @@ -41,7 +41,7 @@ var minMax = function (isMin, args) { if (order.length == 1) { return order[0]; } - args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", "); + args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? "," : ", "); return new(Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); }; functionRegistry.addMultiple({ diff --git a/lib/less/import-manager.js b/lib/less/import-manager.js index 00bedc3b8..ac550a997 100644 --- a/lib/less/import-manager.js +++ b/lib/less/import-manager.js @@ -12,49 +12,49 @@ module.exports = function(environment) { // 'entryPath' - absolute path to the entry file // 'reference' - whether the file should not be output and only output parts that are referenced - var ImportManager = function(env, rootFileInfo) { + var ImportManager = function(context, rootFileInfo) { this.rootFilename = rootFileInfo.filename; - this.paths = env.paths || []; // Search paths, when importing + this.paths = context.paths || []; // Search paths, when importing this.contents = {}; // map - filename to contents of all the files this.contentsIgnoredChars = {}; // map - filename to lines at the begining of each file to ignore - this.mime = env.mime; + this.mime = context.mime; this.error = null; - this.env = env; + this.context = context; // Deprecated? Unused outside of here, could be useful. this.queue = []; // Files which haven't been imported yet this.files = []; // Holds the imported parse trees. }; ImportManager.prototype.push = function (path, currentFileInfo, importOptions, callback) { - var parserImports = this; + var importManager = this; this.queue.push(path); var fileParsedFunc = function (e, root, fullPath) { - parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue + importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue - var importedEqualsRoot = fullPath === parserImports.rootFilename; + var importedEqualsRoot = fullPath === importManager.rootFilename; - parserImports.files[fullPath] = root; + importManager.files[fullPath] = root; - if (e && !parserImports.error) { parserImports.error = e; } + if (e && !importManager.error) { importManager.error = e; } callback(e, root, importedEqualsRoot, fullPath); }; var newFileInfo = { - relativeUrls: this.env.relativeUrls, + relativeUrls: this.context.relativeUrls, entryPath: currentFileInfo.entryPath, rootpath: currentFileInfo.rootpath, rootFilename: currentFileInfo.rootFilename }; - var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.env, environment); + var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment); if (!fileManager) { fileParsedFunc({ message: "Could not find a file-manager for " + path }); return; } - fileManager.loadFile(path, currentFileInfo.currentDirectory, this.env, environment) + fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment) .then(function loadFileCallback(loadedFile) { var resolvedFilename = loadedFile.filename, contents = loadedFile.contents; @@ -69,17 +69,17 @@ module.exports = function(environment) { // then rootpath should become 'less/../' newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename); if(newFileInfo.relativeUrls) { - newFileInfo.rootpath = fileManager.join((parserImports.env.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + newFileInfo.rootpath = fileManager.join((importManager.context.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) { newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath); } } newFileInfo.filename = resolvedFilename; - var newEnv = new contexts.parseEnv(parserImports.env); + var newEnv = new contexts.Parse(importManager.context); newEnv.processImports = false; - parserImports.contents[resolvedFilename] = contents; + importManager.contents[resolvedFilename] = contents; if (currentFileInfo.reference || importOptions.reference) { newFileInfo.reference = true; @@ -88,7 +88,7 @@ module.exports = function(environment) { if (importOptions.inline) { fileParsedFunc(null, contents, resolvedFilename); } else { - new Parser(newEnv, parserImports, newFileInfo).parse(contents, function (e, root) { + new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) { fileParsedFunc(e, root, resolvedFilename); }); } diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index 43a6ba1e9..b04392ba1 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -37,7 +37,7 @@ var LessError = require('../less-error'), // It also takes care of moving all the indices forwards. // // -var Parser = function Parser(env, imports, fileInfo) { +var Parser = function Parser(context, imports, fileInfo) { var parsers, parserInput = getParserInput(); @@ -112,7 +112,7 @@ var Parser = function Parser(env, imports, fileInfo) { // with the `root` property set to true, so no `{}` are // output. The callback is called when the input is parsed. try { - parserInput.start(str, env.chunkInput, function fail(msg, index) { + parserInput.start(str, context.chunkInput, function fail(msg, index) { throw new(LessError)({ index: index, type: 'Parse', @@ -175,7 +175,7 @@ var Parser = function Parser(env, imports, fileInfo) { } }; - if (env.processImports !== false) { + if (context.processImports !== false) { new visitors.ImportVisitor(imports, finish) .run(root); } else { @@ -1000,7 +1000,7 @@ var Parser = function Parser(env, imports, fileInfo) { parserInput.save(); - if (env.dumpLineNumbers) { + if (context.dumpLineNumbers) { debugInfo = getDebugInfo(parserInput.i); } @@ -1023,8 +1023,8 @@ var Parser = function Parser(env, imports, fileInfo) { if (selectors && (rules = this.block())) { parserInput.forget(); - var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports); - if (env.dumpLineNumbers) { + var ruleset = new(tree.Ruleset)(selectors, rules, context.strictImports); + if (context.dumpLineNumbers) { ruleset.debugInfo = debugInfo; } return ruleset; @@ -1056,7 +1056,7 @@ var Parser = function Parser(env, imports, fileInfo) { // prefer to try to parse first if its a variable or we are compressing // but always fallback on the other one - var tryValueFirst = !tryAnonymous && (env.compress || isVariable); + var tryValueFirst = !tryAnonymous && (context.compress || isVariable); if (tryValueFirst) { value = this.value(); @@ -1221,7 +1221,7 @@ var Parser = function Parser(env, imports, fileInfo) { media: function () { var features, rules, media, debugInfo; - if (env.dumpLineNumbers) { + if (context.dumpLineNumbers) { debugInfo = getDebugInfo(parserInput.i); } @@ -1231,7 +1231,7 @@ var Parser = function Parser(env, imports, fileInfo) { rules = this.block(); if (rules) { media = new(tree.Media)(rules, features, parserInput.i, fileInfo); - if (env.dumpLineNumbers) { + if (context.dumpLineNumbers) { media.debugInfo = debugInfo; } return media; @@ -1334,7 +1334,7 @@ var Parser = function Parser(env, imports, fileInfo) { if (rules || (!hasBlock && value && parserInput.$char(';'))) { parserInput.forget(); return new(tree.Directive)(name, value, rules, index, fileInfo, - env.dumpLineNumbers ? getDebugInfo(index) : null); + context.dumpLineNumbers ? getDebugInfo(index) : null); } parserInput.restore("directive options not recognised"); diff --git a/lib/less/render.js b/lib/less/render.js index 50d88999a..6159df21e 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -20,7 +20,7 @@ module.exports = function(environment, ParseTree, ImportManager) { callback(error); }); } else { - var env = new contexts.parseEnv(options), + var context = new contexts.Parse(options), rootFileInfo; if (options.rootFileInfo) { @@ -30,16 +30,16 @@ module.exports = function(environment, ParseTree, ImportManager) { var entryPath = filename.replace(/[^\/\\]*$/, ""); rootFileInfo = { filename: filename, - relativeUrls: env.relativeUrls, - rootpath: env.rootpath || "", + relativeUrls: context.relativeUrls, + rootpath: context.rootpath || "", currentDirectory: entryPath, entryPath: entryPath, rootFilename: filename }; } - var imports = new ImportManager(env, rootFileInfo); - var parser = new Parser(env, imports, rootFileInfo); + var imports = new ImportManager(context, rootFileInfo); + var parser = new Parser(context, imports, rootFileInfo); return new PromiseConstructor(function (resolve, reject) { parser.parse(input, function (e, root) { diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 5f4c9de60..6c78a4763 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -96,7 +96,7 @@ module.exports = function (environment) { return this._css.length === 0; }; - SourceMapOutput.prototype.toCSS = function(env) { + SourceMapOutput.prototype.toCSS = function(context) { this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); if (this._outputSourceFiles) { @@ -112,7 +112,7 @@ module.exports = function (environment) { } } - this._rootNode.genCSS(env, this); + this._rootNode.genCSS(context, this); if (this._css.length > 0) { var sourceMapURL, diff --git a/lib/less/transform-tree.js b/lib/less/transform-tree.js index 7c88bb019..2a0c69f8e 100644 --- a/lib/less/transform-tree.js +++ b/lib/less/transform-tree.js @@ -6,7 +6,7 @@ module.exports = function(root, options) { options = options || {}; var evaldRoot, variables = options.variables, - evalEnv = new contexts.evalEnv(options); + evalEnv = new contexts.Eval(options); // // Allows setting variables with a hash, so: diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js index 9d4d5fa63..82a4ce5e8 100644 --- a/lib/less/tree/alpha.js +++ b/lib/less/tree/alpha.js @@ -9,15 +9,15 @@ Alpha.prototype.type = "Alpha"; Alpha.prototype.accept = function (visitor) { this.value = visitor.visit(this.value); }; -Alpha.prototype.eval = function (env) { - if (this.value.eval) { return new Alpha(this.value.eval(env)); } +Alpha.prototype.eval = function (context) { + if (this.value.eval) { return new Alpha(this.value.eval(context)); } return this; }; -Alpha.prototype.genCSS = function (env, output) { +Alpha.prototype.genCSS = function (context, output) { output.add("alpha(opacity="); if (this.value.genCSS) { - this.value.genCSS(env, output); + this.value.genCSS(context, output); } else { output.add(this.value); } diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 0d81a3f58..bf30c4af0 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -18,7 +18,7 @@ Anonymous.prototype.compare = function (other) { Anonymous.prototype.isRulesetLike = function() { return this.rulesetLike; }; -Anonymous.prototype.genCSS = function (env, output) { +Anonymous.prototype.genCSS = function (context, output) { output.add(this.value, this.currentFileInfo, this.index, this.mapLines); }; module.exports = Anonymous; diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index c9492ce6a..d647b4eca 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -10,16 +10,16 @@ Assignment.prototype.type = "Assignment"; Assignment.prototype.accept = function (visitor) { this.value = visitor.visit(this.value); }; -Assignment.prototype.eval = function (env) { +Assignment.prototype.eval = function (context) { if (this.value.eval) { - return new(Assignment)(this.key, this.value.eval(env)); + return new(Assignment)(this.key, this.value.eval(context)); } return this; }; -Assignment.prototype.genCSS = function (env, output) { +Assignment.prototype.genCSS = function (context, output) { output.add(this.key + '='); if (this.value.genCSS) { - this.value.genCSS(env, output); + this.value.genCSS(context, output); } else { output.add(this.value); } diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js index 67f336e4f..359631542 100644 --- a/lib/less/tree/attribute.js +++ b/lib/less/tree/attribute.js @@ -7,19 +7,19 @@ var Attribute = function (key, op, value) { }; Attribute.prototype = new Node(); Attribute.prototype.type = "Attribute"; -Attribute.prototype.eval = function (env) { - return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key, - this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value); +Attribute.prototype.eval = function (context) { + return new(Attribute)(this.key.eval ? this.key.eval(context) : this.key, + this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value); }; -Attribute.prototype.genCSS = function (env, output) { - output.add(this.toCSS(env)); +Attribute.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context)); }; -Attribute.prototype.toCSS = function (env) { - var value = this.key.toCSS ? this.key.toCSS(env) : this.key; +Attribute.prototype.toCSS = function (context) { + var value = this.key.toCSS ? this.key.toCSS(context) : this.key; if (this.op) { value += this.op; - value += (this.value.toCSS ? this.value.toCSS(env) : this.value); + value += (this.value.toCSS ? this.value.toCSS(context) : this.value); } return '[' + value + ']'; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index ef7199b2e..34a4e0ce5 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -29,9 +29,9 @@ Call.prototype.accept = function (visitor) { // we try to pass a variable to a function, like: `saturate(@color)`. // The function should receive the value, not the variable. // -Call.prototype.eval = function (env) { - var args = this.args.map(function (a) { return a.eval(env); }), - result, funcCaller = new FunctionCaller(this.name, env, this.index, this.currentFileInfo); +Call.prototype.eval = function (context) { + var args = this.args.map(function (a) { return a.eval(context); }), + result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo); if (funcCaller.isValid()) { // 1. try { @@ -49,11 +49,11 @@ Call.prototype.eval = function (env) { return new Call(this.name, args, this.index, this.currentFileInfo); }; -Call.prototype.genCSS = function (env, output) { +Call.prototype.genCSS = function (context, output) { output.add(this.name + "(", this.currentFileInfo, this.index); for(var i = 0; i < this.args.length; i++) { - this.args[i].genCSS(env, output); + this.args[i].genCSS(context, output); if (i + 1 < this.args.length) { output.add(", "); } diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index c51adf1ea..de903c582 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -50,11 +50,11 @@ Color.prototype.luma = function () { return 0.2126 * r + 0.7152 * g + 0.0722 * b; }; -Color.prototype.genCSS = function (env, output) { - output.add(this.toCSS(env)); +Color.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context)); }; -Color.prototype.toCSS = function (env, doNotCompress) { - var compress = env && env.compress && !doNotCompress, color, alpha; +Color.prototype.toCSS = function (context, doNotCompress) { + var compress = context && context.compress && !doNotCompress, color, alpha; // `keyword` is set if this color was originally // converted from a named color string so we need @@ -67,7 +67,7 @@ Color.prototype.toCSS = function (env, doNotCompress) { // 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. - alpha = this.fround(env, this.alpha); + alpha = this.fround(context, this.alpha); if (alpha < 1) { return "rgba(" + this.rgb.map(function (c) { return clamp(Math.round(c), 255); @@ -95,11 +95,11 @@ Color.prototype.toCSS = function (env, doNotCompress) { // our result, in the form of an integer triplet, // we create a new Color node to hold the result. // -Color.prototype.operate = function (env, op, other) { +Color.prototype.operate = function (context, op, other) { var rgb = []; var alpha = this.alpha * (1 - other.alpha) + other.alpha; for (var c = 0; c < 3; c++) { - rgb[c] = this._operate(env, op, this.rgb[c], other.rgb[c]); + rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]); } return new(Color)(rgb, alpha); }; diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js index 784933c65..5342dc04d 100644 --- a/lib/less/tree/combinator.js +++ b/lib/less/tree/combinator.js @@ -14,8 +14,8 @@ var _noSpaceCombinators = { ' ': true, '|': true }; -Combinator.prototype.genCSS = function (env, output) { - var spaceOrEmpty = (env.compress || _noSpaceCombinators[this.value]) ? '' : ' '; +Combinator.prototype.genCSS = function (context, output) { + var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' '; output.add(spaceOrEmpty + this.value + spaceOrEmpty); }; module.exports = Combinator; diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index f0989709f..25842a547 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -8,15 +8,15 @@ var Comment = function (value, isLineComment, index, currentFileInfo) { }; Comment.prototype = new Node(); Comment.prototype.type = "Comment"; -Comment.prototype.genCSS = function (env, output) { +Comment.prototype.genCSS = function (context, output) { if (this.debugInfo) { - output.add(getDebugInfo(env, this), this.currentFileInfo, this.index); + output.add(getDebugInfo(context, this), this.currentFileInfo, this.index); } output.add(this.value); }; -Comment.prototype.isSilent = function(env) { +Comment.prototype.isSilent = function(context) { var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), - isCompressed = env.compress && this.value[2] !== "!"; + isCompressed = context.compress && this.value[2] !== "!"; return this.isLineComment || isReference || isCompressed; }; Comment.prototype.markReferenced = function () { diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index ee65e8734..a451593d6 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -13,7 +13,7 @@ Condition.prototype.accept = function (visitor) { this.lvalue = visitor.visit(this.lvalue); this.rvalue = visitor.visit(this.rvalue); }; -Condition.prototype.eval = function (env) { +Condition.prototype.eval = function (context) { var result = (function (op, a, b) { switch (op) { case 'and': return a && b; @@ -26,7 +26,7 @@ Condition.prototype.eval = function (env) { default: return false; } } - }) (this.op, this.lvalue.eval(env), this.rvalue.eval(env)); + }) (this.op, this.lvalue.eval(context), this.rvalue.eval(context)); return this.negate ? !result : result; }; diff --git a/lib/less/tree/debug-info.js b/lib/less/tree/debug-info.js index 5af873fc6..160301997 100644 --- a/lib/less/tree/debug-info.js +++ b/lib/less/tree/debug-info.js @@ -1,7 +1,7 @@ -var debugInfo = function(env, ctx, lineSeperator) { +var debugInfo = function(context, ctx, lineSeperator) { var result=""; - if (env.dumpLineNumbers && !env.compress) { - switch(env.dumpLineNumbers) { + if (context.dumpLineNumbers && !context.compress) { + switch(context.dumpLineNumbers) { case 'comments': result = debugInfo.asComment(ctx); break; diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js index f0c81e7c3..01bbbc759 100644 --- a/lib/less/tree/detached-ruleset.js +++ b/lib/less/tree/detached-ruleset.js @@ -11,11 +11,11 @@ DetachedRuleset.prototype.evalFirst = true; DetachedRuleset.prototype.accept = function (visitor) { this.ruleset = visitor.visit(this.ruleset); }; -DetachedRuleset.prototype.eval = function (env) { - var frames = this.frames || env.frames.slice(0); +DetachedRuleset.prototype.eval = function (context) { + var frames = this.frames || context.frames.slice(0); return new DetachedRuleset(this.ruleset, frames); }; -DetachedRuleset.prototype.callEval = function (env) { - return this.ruleset.eval(this.frames ? new(contexts.evalEnv)(env, this.frames.concat(env.frames)) : env); +DetachedRuleset.prototype.callEval = function (context) { + return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context); }; module.exports = DetachedRuleset; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index ce59f2716..c5bf45369 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -17,18 +17,18 @@ Dimension.prototype.type = "Dimension"; Dimension.prototype.accept = function (visitor) { this.unit = visitor.visit(this.unit); }; -Dimension.prototype.eval = function (env) { +Dimension.prototype.eval = function (context) { return this; }; Dimension.prototype.toColor = function () { return new(Color)([this.value, this.value, this.value]); }; -Dimension.prototype.genCSS = function (env, output) { - if ((env && env.strictUnits) && !this.unit.isSingular()) { +Dimension.prototype.genCSS = function (context, output) { + if ((context && context.strictUnits) && !this.unit.isSingular()) { throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); } - var value = this.fround(env, this.value), + var value = this.fround(context, this.value), strValue = String(value); if (value !== 0 && value < 0.000001 && value > -0.000001) { @@ -36,7 +36,7 @@ Dimension.prototype.genCSS = function (env, output) { strValue = value.toFixed(20).replace(/0+$/, ""); } - if (env && env.compress) { + if (context && context.compress) { // Zero values doesn't need a unit if (value === 0 && this.unit.isLength()) { output.add(strValue); @@ -50,15 +50,15 @@ Dimension.prototype.genCSS = function (env, output) { } output.add(strValue); - this.unit.genCSS(env, output); + this.unit.genCSS(context, output); }; // In an operation between two Dimensions, // we default to the first Dimension's unit, // so `1px + 2` will yield `3px`. -Dimension.prototype.operate = function (env, op, other) { +Dimension.prototype.operate = function (context, op, other) { /*jshint noempty:false */ - var value = this._operate(env, op, this.value, other.value), + var value = this._operate(context, op, this.value, other.value), unit = this.unit.clone(); if (op === '+' || op === '-') { @@ -70,12 +70,12 @@ Dimension.prototype.operate = function (env, op, other) { } else { other = other.convertTo(this.unit.usedUnits()); - if(env.strictUnits && other.unit.toString() !== unit.toString()) { + if(context.strictUnits && other.unit.toString() !== unit.toString()) { throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + "' and '" + other.unit.toString() + "'."); } - value = this._operate(env, op, this.value, other.value); + value = this._operate(context, op, this.value, other.value); } } else if (op === '*') { unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index c69eee269..37c5cb1e7 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -30,26 +30,26 @@ Directive.prototype.isRulesetLike = function() { Directive.prototype.isCharset = function() { return "@charset" === this.name; }; -Directive.prototype.genCSS = function (env, output) { +Directive.prototype.genCSS = function (context, output) { var value = this.value, rules = this.rules; output.add(this.name, this.currentFileInfo, this.index); if (value) { output.add(' '); - value.genCSS(env, output); + value.genCSS(context, output); } if (rules) { - this.outputRuleset(env, output, [rules]); + this.outputRuleset(context, output, [rules]); } else { output.add(';'); } }; -Directive.prototype.eval = function (env) { +Directive.prototype.eval = function (context) { var value = this.value, rules = this.rules; if (value) { - value = value.eval(env); + value = value.eval(context); } if (rules) { - rules = rules.eval(env); + rules = rules.eval(context); rules.root = true; } return new(Directive)(this.name, value, rules, @@ -70,35 +70,35 @@ Directive.prototype.markReferenced = function () { } } }; -Directive.prototype.outputRuleset = function (env, output, rules) { +Directive.prototype.outputRuleset = function (context, output, rules) { var ruleCnt = rules.length, i; - env.tabLevel = (env.tabLevel | 0) + 1; + context.tabLevel = (context.tabLevel | 0) + 1; // Compressed - if (env.compress) { + if (context.compress) { output.add('{'); for (i = 0; i < ruleCnt; i++) { - rules[i].genCSS(env, output); + rules[i].genCSS(context, output); } output.add('}'); - env.tabLevel--; + context.tabLevel--; return; } // Non-compressed - var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; + var tabSetStr = '\n' + Array(context.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; if (!ruleCnt) { output.add(" {" + tabSetStr + '}'); } else { output.add(" {" + tabRuleStr); - rules[0].genCSS(env, output); + rules[0].genCSS(context, output); for (i = 1; i < ruleCnt; i++) { output.add(tabRuleStr); - rules[i].genCSS(env, output); + rules[i].genCSS(context, output); } output.add(tabSetStr + '}'); } - env.tabLevel--; + context.tabLevel--; }; module.exports = Directive; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index f62f65614..df46b00ca 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -25,29 +25,29 @@ Element.prototype.accept = function (visitor) { this.value = visitor.visit(value); } }; -Element.prototype.eval = function (env) { +Element.prototype.eval = function (context) { return new(Element)(this.combinator, - this.value.eval ? this.value.eval(env) : this.value, + this.value.eval ? this.value.eval(context) : this.value, this.index, this.currentFileInfo); }; -Element.prototype.genCSS = function (env, output) { - output.add(this.toCSS(env), this.currentFileInfo, this.index); +Element.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context), this.currentFileInfo, this.index); }; -Element.prototype.toCSS = function (env) { - env = env || {}; - var value = this.value, firstSelector = env.firstSelector; +Element.prototype.toCSS = function (context) { + context = context || {}; + var value = this.value, firstSelector = context.firstSelector; if (value instanceof Paren) { // selector in parens should not be affected by outer selector // flags (breaks only interpolated selectors - see #1973) - env.firstSelector = true; + context.firstSelector = true; } - value = value.toCSS ? value.toCSS(env) : value; - env.firstSelector = firstSelector; + value = value.toCSS ? value.toCSS(context) : value; + context.firstSelector = firstSelector; if (value === '' && this.combinator.value.charAt(0) === '&') { return ''; } else { - return this.combinator.toCSS(env) + value; + return this.combinator.toCSS(context) + value; } }; module.exports = Element; diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 2f25c7ef2..59e8a40e1 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -10,36 +10,36 @@ Expression.prototype.accept = function (visitor) { this.value = visitor.visitArray(this.value); } }; -Expression.prototype.eval = function (env) { +Expression.prototype.eval = function (context) { var returnValue, inParenthesis = this.parens && !this.parensInOp, doubleParen = false; if (inParenthesis) { - env.inParenthesis(); + context.inParenthesis(); } if (this.value.length > 1) { returnValue = new(Expression)(this.value.map(function (e) { - return e.eval(env); + return e.eval(context); })); } else if (this.value.length === 1) { if (this.value[0].parens && !this.value[0].parensInOp) { doubleParen = true; } - returnValue = this.value[0].eval(env); + returnValue = this.value[0].eval(context); } else { returnValue = this; } if (inParenthesis) { - env.outOfParenthesis(); + context.outOfParenthesis(); } - if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) { + if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) { returnValue = new(Paren)(returnValue); } return returnValue; }; -Expression.prototype.genCSS = function (env, output) { +Expression.prototype.genCSS = function (context, output) { for(var i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); + this.value[i].genCSS(context, output); if (i + 1 < this.value.length) { output.add(" "); } diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js index caa7016fc..7758ea1f1 100644 --- a/lib/less/tree/extend.js +++ b/lib/less/tree/extend.js @@ -25,10 +25,10 @@ Extend.prototype.type = "Extend"; Extend.prototype.accept = function (visitor) { this.selector = visitor.visit(this.selector); }; -Extend.prototype.eval = function (env) { - return new(Extend)(this.selector.eval(env), this.option, this.index); +Extend.prototype.eval = function (context) { + return new(Extend)(this.selector.eval(context), this.option, this.index); }; -Extend.prototype.clone = function (env) { +Extend.prototype.clone = function (context) { return new(Extend)(this.selector, this.option, this.index); }; Extend.prototype.findSelfSelectors = function (selectors) { diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 917199054..f1e5cae26 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -54,13 +54,13 @@ Import.prototype.accept = function (visitor) { this.root = visitor.visit(this.root); } }; -Import.prototype.genCSS = function (env, output) { +Import.prototype.genCSS = function (context, output) { if (this.css) { output.add("@import ", this.currentFileInfo, this.index); - this.path.genCSS(env, output); + this.path.genCSS(context, output); if (this.features) { output.add(" "); - this.features.genCSS(env, output); + this.features.genCSS(context, output); } output.add(';'); } @@ -74,32 +74,32 @@ Import.prototype.getPath = function () { } return null; }; -Import.prototype.evalForImport = function (env) { +Import.prototype.evalForImport = function (context) { var path = this.path; if (path instanceof URL) { path = path.value; } - return new(Import)(path.eval(env), this.features, this.options, this.index, this.currentFileInfo); + return new(Import)(path.eval(context), this.features, this.options, this.index, this.currentFileInfo); }; -Import.prototype.evalPath = function (env) { - var path = this.path.eval(env); +Import.prototype.evalPath = function (context) { + var path = this.path.eval(context); var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; if (!(path instanceof URL)) { if (rootpath) { var pathValue = path.value; // Add the base path if the import is relative - if (pathValue && env.isPathRelative(pathValue)) { + if (pathValue && context.isPathRelative(pathValue)) { path.value = rootpath +pathValue; } } - path.value = env.normalizePath(path.value); + path.value = context.normalizePath(path.value); } return path; }; -Import.prototype.eval = function (env) { - var ruleset, features = this.features && this.features.eval(env); +Import.prototype.eval = function (context) { + var ruleset, features = this.features && this.features.eval(context); if (this.skip) { if (typeof this.skip === "function") { @@ -114,7 +114,7 @@ Import.prototype.eval = function (env) { var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); return this.features ? new(Media)([contents], this.features.value) : [contents]; } else if (this.css) { - var newImport = new(Import)(this.evalPath(env), features, this.options, this.index); + var newImport = new(Import)(this.evalPath(context), features, this.options, this.index); if (!newImport.css && this.error) { throw this.error; } @@ -122,7 +122,7 @@ Import.prototype.eval = function (env) { } else { ruleset = new(Ruleset)(null, this.root.rules.slice(0)); - ruleset.evalImports(env); + ruleset.evalImports(context); return this.features ? new(Media)(ruleset.rules, this.features.value) : ruleset.rules; } diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index 05913b8b2..b951bfa96 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -11,8 +11,8 @@ var JavaScript = function (string, escaped, index, currentFileInfo) { }; JavaScript.prototype = new JsEvalNode(); JavaScript.prototype.type = "JavaScript"; -JavaScript.prototype.eval = function(env) { - var result = this.evaluateJavaScript(this.expression, env); +JavaScript.prototype.eval = function(context) { + var result = this.evaluateJavaScript(this.expression, context); if (typeof(result) === 'number') { return new(Dimension)(result); diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js index 2df63bb42..c9f86c733 100644 --- a/lib/less/tree/js-eval-node.js +++ b/lib/less/tree/js-eval-node.js @@ -5,19 +5,19 @@ var JsEvalNode = function() { }; JsEvalNode.prototype = new Node(); -JsEvalNode.prototype.evaluateJavaScript = function (expression, env) { +JsEvalNode.prototype.evaluateJavaScript = function (expression, context) { var result, that = this, - context = {}; + evalContext = {}; - if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) { + if (context.javascriptEnabled !== undefined && !context.javascriptEnabled) { throw { message: "You are using JavaScript, which has been disabled.", filename: this.currentFileInfo.filename, index: this.index }; } expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return that.jsify(new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env)); + return that.jsify(new(Variable)('@' + name, that.index, that.currentFileInfo).eval(context)); }); try { @@ -28,21 +28,21 @@ JsEvalNode.prototype.evaluateJavaScript = function (expression, env) { index: this.index }; } - var variables = env.frames[0].variables(); + var variables = context.frames[0].variables(); for (var k in variables) { if (variables.hasOwnProperty(k)) { /*jshint loopfunc:true */ - context[k.slice(1)] = { + evalContext[k.slice(1)] = { value: variables[k].value, toJS: function () { - return this.value.eval(env).toCSS(); + return this.value.eval(context).toCSS(); } }; } } try { - result = expression.call(context); + result = expression.call(evalContext); } catch (e) { throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , filename: this.currentFileInfo.filename, diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 80677a468..cbf3db4ff 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -3,7 +3,7 @@ var Node = require("./node"); var Keyword = function (value) { this.value = value; }; Keyword.prototype = new Node(); Keyword.prototype.type = "Keyword"; -Keyword.prototype.genCSS = function (env, output) { +Keyword.prototype.genCSS = function (context, output) { if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } output.add(this.value); }; diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 45d008d9b..8ec992770 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -27,15 +27,15 @@ Media.prototype.accept = function (visitor) { this.rules = visitor.visitArray(this.rules); } }; -Media.prototype.genCSS = function (env, output) { +Media.prototype.genCSS = function (context, output) { output.add('@media ', this.currentFileInfo, this.index); - this.features.genCSS(env, output); - this.outputRuleset(env, output, this.rules); + this.features.genCSS(context, output); + this.outputRuleset(context, output, this.rules); }; -Media.prototype.eval = function (env) { - if (!env.mediaBlocks) { - env.mediaBlocks = []; - env.mediaPath = []; +Media.prototype.eval = function (context) { + if (!context.mediaBlocks) { + context.mediaBlocks = []; + context.mediaPath = []; } var media = new(Media)(null, [], this.index, this.currentFileInfo); @@ -44,30 +44,30 @@ Media.prototype.eval = function (env) { media.debugInfo = this.debugInfo; } var strictMathBypass = false; - if (!env.strictMath) { + if (!context.strictMath) { strictMathBypass = true; - env.strictMath = true; + context.strictMath = true; } try { - media.features = this.features.eval(env); + media.features = this.features.eval(context); } finally { if (strictMathBypass) { - env.strictMath = false; + context.strictMath = false; } } - env.mediaPath.push(media); - env.mediaBlocks.push(media); + context.mediaPath.push(media); + context.mediaBlocks.push(media); - env.frames.unshift(this.rules[0]); - media.rules = [this.rules[0].eval(env)]; - env.frames.shift(); + context.frames.unshift(this.rules[0]); + media.rules = [this.rules[0].eval(context)]; + context.frames.shift(); - env.mediaPath.pop(); + context.mediaPath.pop(); - return env.mediaPath.length === 0 ? media.evalTop(env) : - media.evalNested(env); + return context.mediaPath.length === 0 ? media.evalTop(context) : + media.evalNested(context); }; //TODO merge with directive Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); }; @@ -89,24 +89,24 @@ Media.prototype.markReferenced = function () { } } }; -Media.prototype.evalTop = function (env) { +Media.prototype.evalTop = function (context) { var result = this; // Render all dependent Media blocks. - if (env.mediaBlocks.length > 1) { + if (context.mediaBlocks.length > 1) { var selectors = this.emptySelectors(); - result = new(Ruleset)(selectors, env.mediaBlocks); + result = new(Ruleset)(selectors, context.mediaBlocks); result.multiMedia = true; } - delete env.mediaBlocks; - delete env.mediaPath; + delete context.mediaBlocks; + delete context.mediaPath; return result; }; -Media.prototype.evalNested = function (env) { +Media.prototype.evalNested = function (context) { var i, value, - path = env.mediaPath.concat([this]); + path = context.mediaPath.concat([this]); // Extract the media-query conditions separated with `,` (OR). for (i = 0; i < path.length; i++) { diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js index 7def79c9c..b5e93b4e0 100644 --- a/lib/less/tree/mixin-call.js +++ b/lib/less/tree/mixin-call.js @@ -20,7 +20,7 @@ MixinCall.prototype.accept = function (visitor) { this.arguments = visitor.visitArray(this.arguments); } }; -MixinCall.prototype.eval = function (env) { +MixinCall.prototype.eval = function (context) { var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase=-1, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter; @@ -34,11 +34,11 @@ MixinCall.prototype.eval = function (env) { for(p = 0; p < mixinPath.length && conditionResult[f]; p++) { namespace = mixinPath[p]; if (namespace.matchCondition) { - conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, env); + conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context); } } if (mixin.matchCondition) { - conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, env); + conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context); } } if (conditionResult[0] || conditionResult[1]) { @@ -53,13 +53,13 @@ MixinCall.prototype.eval = function (env) { } args = this.arguments && this.arguments.map(function (a) { - return { name: a.name, value: a.value.eval(env) }; + return { name: a.name, value: a.value.eval(context) }; }); - noArgumentsFilter = function(rule) {return rule.matchArgs(null, env);}; + noArgumentsFilter = function(rule) {return rule.matchArgs(null, context);}; - for (i = 0; i < env.frames.length; i++) { - if ((mixins = env.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) { + for (i = 0; i < context.frames.length; i++) { + if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) { isOneFound = true; // To make `default()` function independent of definition order we have two "subpasses" here. @@ -71,8 +71,8 @@ MixinCall.prototype.eval = function (env) { mixin = mixins[m].rule; mixinPath = mixins[m].path; isRecursive = false; - for(f = 0; f < env.frames.length; f++) { - if ((!(mixin instanceof MixinDefinition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) { + for(f = 0; f < context.frames.length; f++) { + if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) { isRecursive = true; break; } @@ -81,7 +81,7 @@ MixinCall.prototype.eval = function (env) { continue; } - if (mixin.matchArgs(args, env)) { + if (mixin.matchArgs(args, context)) { candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)}; if (candidate.group!==defFalseEitherCase) { @@ -122,7 +122,7 @@ MixinCall.prototype.eval = function (env) { mixin.originalRuleset = originalRuleset; } Array.prototype.push.apply( - rules, mixin.evalCall(env, args, this.important).rules); + rules, mixin.evalCall(context, args, this.important).rules); } catch (e) { throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; } diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js index e7a895776..26dc699bc 100644 --- a/lib/less/tree/mixin-definition.js +++ b/lib/less/tree/mixin-definition.js @@ -32,14 +32,14 @@ Definition.prototype.accept = function (visitor) { this.condition = visitor.visit(this.condition); } }; -Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) { +Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) { /*jshint boss:true */ var frame = new(Ruleset)(null, null), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex, argsLength = 0; - mixinEnv = new contexts.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames)); + mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames)); if (args) { args = args.slice(0); @@ -51,8 +51,8 @@ Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) isNamedFound = false; for(j = 0; j < params.length; j++) { if (!evaldArguments[j] && name === params[j].name) { - evaldArguments[j] = arg.value.eval(env); - frame.prependRule(new(Rule)(name, arg.value.eval(env))); + evaldArguments[j] = arg.value.eval(context); + frame.prependRule(new(Rule)(name, arg.value.eval(context))); isNamedFound = true; break; } @@ -78,13 +78,13 @@ Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) if (params[i].variadic) { varargs = []; for (j = argIndex; j < argsLength; j++) { - varargs.push(args[j].value.eval(env)); + varargs.push(args[j].value.eval(context)); } - frame.prependRule(new(Rule)(name, new(Expression)(varargs).eval(env))); + frame.prependRule(new(Rule)(name, new(Expression)(varargs).eval(context))); } else { val = arg && arg.value; if (val) { - val = val.eval(env); + val = val.eval(context); } else if (params[i].value) { val = params[i].value.eval(mixinEnv); frame.resetCache(); @@ -100,7 +100,7 @@ Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) if (params[i].variadic && args) { for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); + evaldArguments[j] = args[j].value.eval(context); } } argIndex++; @@ -108,38 +108,38 @@ Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) return frame; }; -Definition.prototype.eval = function (env) { - return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0)); +Definition.prototype.eval = function (context) { + return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || context.frames.slice(0)); }; -Definition.prototype.evalCall = function (env, args, important) { +Definition.prototype.evalCall = function (context, args, important) { var _arguments = [], - mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames, - frame = this.evalParams(env, new(contexts.evalEnv)(env, mixinFrames), args, _arguments), + mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames, + frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments), rules, ruleset; - frame.prependRule(new(Rule)('@arguments', new(Expression)(_arguments).eval(env))); + frame.prependRule(new(Rule)('@arguments', new Expression(_arguments).eval(context))); rules = this.rules.slice(0); ruleset = new(Ruleset)(null, rules); ruleset.originalRuleset = this; - ruleset = ruleset.eval(new(contexts.evalEnv)(env, [this, frame].concat(mixinFrames))); + ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames))); if (important) { ruleset = this.makeImportant.apply(ruleset); } return ruleset; }; -Definition.prototype.matchCondition = function (args, env) { +Definition.prototype.matchCondition = function (args, context) { if (this.condition && !this.condition.eval( - new(contexts.evalEnv)(env, - [this.evalParams(env, new(contexts.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables + new contexts.Eval(context, + [this.evalParams(context, new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])] // the parameter variables .concat(this.frames) // the parent namespace/mixin frames - .concat(env.frames)))) { // the current environment frames + .concat(context.frames)))) { // the current environment frames return false; } return true; }; -Definition.prototype.matchArgs = function (args, env) { +Definition.prototype.matchArgs = function (args, context) { var argsLength = (args && args.length) || 0, len; if (! this.variadic) { @@ -153,7 +153,7 @@ Definition.prototype.matchArgs = function (args, env) { for (var i = 0; i < len; i++) { if (!this.params[i].name && !this.params[i].variadic) { - if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) { + if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) { return false; } } diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index 423293012..30b1a98ea 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -7,14 +7,14 @@ var Negative = function (node) { }; Negative.prototype = new Node(); Negative.prototype.type = "Negative"; -Negative.prototype.genCSS = function (env, output) { +Negative.prototype.genCSS = function (context, output) { output.add('-'); - this.value.genCSS(env, output); + this.value.genCSS(context, output); }; -Negative.prototype.eval = function (env) { - if (env.isMathOn()) { - return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(env); +Negative.prototype.eval = function (context) { + if (context.isMathOn()) { + return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(context); } - return new(Negative)(this.value.eval(env)); + return new(Negative)(this.value.eval(context)); }; module.exports = Negative; diff --git a/lib/less/tree/node.js b/lib/less/tree/node.js index dc14c6593..cc2d64684 100644 --- a/lib/less/tree/node.js +++ b/lib/less/tree/node.js @@ -1,8 +1,8 @@ var Node = function() { }; -Node.prototype.toCSS = function (env) { +Node.prototype.toCSS = function (context) { var strs = []; - this.genCSS(env, { + this.genCSS(context, { add: function(chunk, fileInfo, index) { strs.push(chunk); }, @@ -12,14 +12,14 @@ Node.prototype.toCSS = function (env) { }); return strs.join(''); }; -Node.prototype.genCSS = function (env, output) { +Node.prototype.genCSS = function (context, output) { output.add(this.value); }; Node.prototype.accept = function (visitor) { this.value = visitor.visit(this.value); }; Node.prototype.eval = function () { return this; }; -Node.prototype._operate = function (env, op, a, b) { +Node.prototype._operate = function (context, op, a, b) { switch (op) { case '+': return a + b; case '-': return a - b; @@ -27,8 +27,8 @@ Node.prototype._operate = function (env, op, a, b) { case '/': return a / b; } }; -Node.prototype.fround = function(env, value) { - var precision = env && env.numPrecision; +Node.prototype.fround = function(context, value) { + var precision = context && context.numPrecision; //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... return (precision == null) ? value : Number((value + 2e-16).toFixed(precision)); }; diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index a76dda9de..a95021a82 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -12,11 +12,11 @@ Operation.prototype.type = "Operation"; Operation.prototype.accept = function (visitor) { this.operands = visitor.visit(this.operands); }; -Operation.prototype.eval = function (env) { - var a = this.operands[0].eval(env), - b = this.operands[1].eval(env); +Operation.prototype.eval = function (context) { + var a = this.operands[0].eval(context), + b = this.operands[1].eval(context); - if (env.isMathOn()) { + if (context.isMathOn()) { if (a instanceof Dimension && b instanceof Color) { a = a.toColor(); } @@ -28,13 +28,13 @@ Operation.prototype.eval = function (env) { message: "Operation on an invalid type" }; } - return a.operate(env, this.op, b); + return a.operate(context, this.op, b); } else { return new(Operation)(this.op, [a, b], this.isSpaced); } }; -Operation.prototype.genCSS = function (env, output) { - this.operands[0].genCSS(env, output); +Operation.prototype.genCSS = function (context, output) { + this.operands[0].genCSS(context, output); if (this.isSpaced) { output.add(" "); } @@ -42,7 +42,7 @@ Operation.prototype.genCSS = function (env, output) { if (this.isSpaced) { output.add(" "); } - this.operands[1].genCSS(env, output); + this.operands[1].genCSS(context, output); }; module.exports = Operation; diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index 54c71fbe0..a6f77921b 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -5,12 +5,12 @@ var Paren = function (node) { }; Paren.prototype = new Node(); Paren.prototype.type = "Paren"; -Paren.prototype.genCSS = function (env, output) { +Paren.prototype.genCSS = function (context, output) { output.add('('); - this.value.genCSS(env, output); + this.value.genCSS(context, output); output.add(')'); }; -Paren.prototype.eval = function (env) { - return new(Paren)(this.value.eval(env)); +Paren.prototype.eval = function (context) { + return new(Paren)(this.value.eval(context)); }; module.exports = Paren; diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 11e646b40..6974ab6a9 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -11,7 +11,7 @@ var Quoted = function (str, content, escaped, index, currentFileInfo) { }; Quoted.prototype = new JsEvalNode(); Quoted.prototype.type = "Quoted"; -Quoted.prototype.genCSS = function (env, output) { +Quoted.prototype.genCSS = function (context, output) { if (!this.escaped) { output.add(this.quote, this.currentFileInfo, this.index); } @@ -20,13 +20,13 @@ Quoted.prototype.genCSS = function (env, output) { output.add(this.quote); } }; -Quoted.prototype.eval = function (env) { +Quoted.prototype.eval = function (context) { var that = this, value = this.value; var javascriptReplacement = function (_, exp) { - return String(that.evaluateJavaScript(exp, env)); + return String(that.evaluateJavaScript(exp, context)); }; var interpolationReplacement = function (_, name) { - var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true); + var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(context, true); return (v instanceof Quoted) ? v.value : v.toCSS(); }; function iterativeReplace(value, regexp, replacementFnc) { diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index fcc53295f..f395c6a64 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -14,45 +14,45 @@ var Rule = function (name, value, important, merge, index, currentFileInfo, inli : (name.charAt && (name.charAt(0) === '@')); }; -function evalName(env, name) { +function evalName(context, name) { var value = "", i, n = name.length, output = {add: function (s) {value += s;}}; for (i = 0; i < n; i++) { - name[i].eval(env).genCSS(env, output); + name[i].eval(context).genCSS(context, output); } return value; } Rule.prototype = new Node(); Rule.prototype.type = "Rule"; -Rule.prototype.genCSS = function (env, output) { - output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index); +Rule.prototype.genCSS = function (context, output) { + output.add(this.name + (context.compress ? ':' : ': '), this.currentFileInfo, this.index); try { - this.value.genCSS(env, output); + this.value.genCSS(context, output); } catch(e) { e.index = this.index; e.filename = this.currentFileInfo.filename; throw e; } - output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index); + output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this.currentFileInfo, this.index); }; -Rule.prototype.eval = function (env) { +Rule.prototype.eval = function (context) { var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable; if (typeof name !== "string") { // expand 'primitive' name directly to get // things faster (~10% for benchmark.less): name = (name.length === 1) && (name[0] instanceof Keyword) - ? name[0].value : evalName(env, name); + ? name[0].value : evalName(context, name); variable = false; // never treat expanded interpolation as new variable name } - if (name === "font" && !env.strictMath) { + if (name === "font" && !context.strictMath) { strictMathBypass = true; - env.strictMath = true; + context.strictMath = true; } try { - evaldValue = this.value.eval(env); + evaldValue = this.value.eval(context); if (!this.variable && evaldValue.type === "DetachedRuleset") { throw { message: "Rulesets cannot be evaluated on a property.", @@ -75,7 +75,7 @@ Rule.prototype.eval = function (env) { } finally { if (strictMathBypass) { - env.strictMath = false; + context.strictMath = false; } } }; diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js index 3a2e77a40..3cb91c982 100644 --- a/lib/less/tree/ruleset-call.js +++ b/lib/less/tree/ruleset-call.js @@ -6,8 +6,8 @@ var RulesetCall = function (variable) { }; RulesetCall.prototype = new Node(); RulesetCall.prototype.type = "RulesetCall"; -RulesetCall.prototype.eval = function (env) { - var detachedRuleset = new(Variable)(this.variable).eval(env); - return detachedRuleset.callEval(env); +RulesetCall.prototype.eval = function (context) { + var detachedRuleset = new(Variable)(this.variable).eval(context); + return detachedRuleset.callEval(context); }; module.exports = RulesetCall; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 0760595b2..74e8fd351 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -26,7 +26,7 @@ Ruleset.prototype.accept = function (visitor) { this.rules = visitor.visitArray(this.rules); } }; -Ruleset.prototype.eval = function (env) { +Ruleset.prototype.eval = function (context) { var thisSelectors = this.selectors, selectors, selCnt, selector, i, hasOnePassingSelector = false; @@ -37,7 +37,7 @@ Ruleset.prototype.eval = function (env) { message: "it is currently only allowed in parametric mixin guards," }); for (i = 0; i < selCnt; i++) { - selector = thisSelectors[i].eval(env); + selector = thisSelectors[i].eval(context); selectors.push(selector); if (selector.evaldCondition) { hasOnePassingSelector = true; @@ -66,19 +66,19 @@ Ruleset.prototype.eval = function (env) { } // push the current ruleset to the frames stack - var envFrames = env.frames; - envFrames.unshift(ruleset); + var ctxFrames = context.frames; + ctxFrames.unshift(ruleset); // currrent selectors - var envSelectors = env.selectors; - if (!envSelectors) { - env.selectors = envSelectors = []; + var ctxSelectors = context.selectors; + if (!ctxSelectors) { + context.selectors = ctxSelectors = []; } - envSelectors.unshift(this.selectors); + ctxSelectors.unshift(this.selectors); // Evaluate imports if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { - ruleset.evalImports(env); + ruleset.evalImports(context); } // Store the frames around mixin definitions, @@ -86,17 +86,17 @@ Ruleset.prototype.eval = function (env) { var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; for (i = 0; i < rsRuleCnt; i++) { if (rsRules[i].evalFirst) { - rsRules[i] = rsRules[i].eval(env); + rsRules[i] = rsRules[i].eval(context); } } - var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0; + var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0; // Evaluate mixin calls. for (i = 0; i < rsRuleCnt; i++) { if (rsRules[i].type === "MixinCall") { /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).filter(function(r) { + rules = rsRules[i].eval(context).filter(function(r) { if ((r instanceof Rule) && r.variable) { // do not pollute the scope if the variable is // already there. consider returning false here @@ -111,7 +111,7 @@ Ruleset.prototype.eval = function (env) { ruleset.resetCache(); } else if (rsRules[i].type === "RulesetCall") { /*jshint loopfunc:true */ - rules = rsRules[i].eval(env).rules.filter(function(r) { + rules = rsRules[i].eval(context).rules.filter(function(r) { if ((r instanceof Rule) && r.variable) { // do not pollute the scope at all return false; @@ -129,7 +129,7 @@ Ruleset.prototype.eval = function (env) { for (i = 0; i < rsRules.length; i++) { rule = rsRules[i]; if (!rule.evalFirst) { - rsRules[i] = rule = rule.eval ? rule.eval(env) : rule; + rsRules[i] = rule = rule.eval ? rule.eval(context) : rule; } } @@ -153,24 +153,24 @@ Ruleset.prototype.eval = function (env) { } // Pop the stack - envFrames.shift(); - envSelectors.shift(); + ctxFrames.shift(); + ctxSelectors.shift(); - if (env.mediaBlocks) { - for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) { - env.mediaBlocks[i].bubbleSelectors(selectors); + if (context.mediaBlocks) { + for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) { + context.mediaBlocks[i].bubbleSelectors(selectors); } } return ruleset; }; -Ruleset.prototype.evalImports = function(env) { +Ruleset.prototype.evalImports = function(context) { var rules = this.rules, i, importRules; if (!rules) { return; } for (i = 0; i < rules.length; i++) { if (rules[i].type === "Import") { - importRules = rules[i].eval(env); + importRules = rules[i].eval(context); if (importRules && importRules.length) { rules.splice.apply(rules, [i, 1].concat(importRules)); i+= importRules.length-1; @@ -194,15 +194,15 @@ Ruleset.prototype.matchArgs = function (args) { return !args || args.length === 0; }; // lets you call a css selector with a guard -Ruleset.prototype.matchCondition = function (args, env) { +Ruleset.prototype.matchCondition = function (args, context) { var lastSelector = this.selectors[this.selectors.length-1]; if (!lastSelector.evaldCondition) { return false; } if (lastSelector.condition && !lastSelector.condition.eval( - new(contexts.evalEnv)(env, - env.frames))) { + new contexts.Eval(context, + context.frames))) { return false; } return true; @@ -276,7 +276,7 @@ Ruleset.prototype.find = function (selector, self, filter) { this._lookups[key] = rules; return rules; }; -Ruleset.prototype.genCSS = function (env, output) { +Ruleset.prototype.genCSS = function (context, output) { var i, j, charsetRuleNodes = [], ruleNodes = [], @@ -286,14 +286,14 @@ Ruleset.prototype.genCSS = function (env, output) { rule, path; - env.tabLevel = (env.tabLevel || 0); + context.tabLevel = (context.tabLevel || 0); if (!this.root) { - env.tabLevel++; + context.tabLevel++; } - var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "), - tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "), + var tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(" "), + tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "), sep; function isRulesetLikeNode(rule, root) { @@ -330,7 +330,7 @@ Ruleset.prototype.genCSS = function (env, output) { // If this is the root node, we don't render // a selector, or {}. if (!this.root) { - debugInfo = getDebugInfo(env, this, tabSetStr); + debugInfo = getDebugInfo(context, this, tabSetStr); if (debugInfo) { output.add(debugInfo); @@ -340,23 +340,23 @@ Ruleset.prototype.genCSS = function (env, output) { var paths = this.paths, pathCnt = paths.length, pathSubCnt; - sep = env.compress ? ',' : (',\n' + tabSetStr); + sep = context.compress ? ',' : (',\n' + tabSetStr); for (i = 0; i < pathCnt; i++) { path = paths[i]; if (!(pathSubCnt = path.length)) { continue; } if (i > 0) { output.add(sep); } - env.firstSelector = true; - path[0].genCSS(env, output); + context.firstSelector = true; + path[0].genCSS(context, output); - env.firstSelector = false; + context.firstSelector = false; for (j = 1; j < pathSubCnt; j++) { - path[j].genCSS(env, output); + path[j].genCSS(context, output); } } - output.add((env.compress ? '{' : ' {\n') + tabRuleStr); + output.add((context.compress ? '{' : ' {\n') + tabRuleStr); } // Compile rules and rulesets @@ -366,39 +366,39 @@ Ruleset.prototype.genCSS = function (env, output) { // @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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { - env.lastRule = true; + context.lastRule = true; } if (rule.genCSS) { - rule.genCSS(env, output); + rule.genCSS(context, output); } else if (rule.value) { output.add(rule.value.toString()); } - if (!env.lastRule) { - output.add(env.compress ? '' : ('\n' + tabRuleStr)); + if (!context.lastRule) { + output.add(context.compress ? '' : ('\n' + tabRuleStr)); } else { - env.lastRule = false; + context.lastRule = false; } } if (!this.root) { - output.add((env.compress ? '}' : '\n' + tabSetStr + '}')); - env.tabLevel--; + output.add((context.compress ? '}' : '\n' + tabSetStr + '}')); + context.tabLevel--; } - sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); + sep = (context.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); rulesetNodeCnt = rulesetNodes.length; if (rulesetNodeCnt) { if (ruleNodes.length && sep) { output.add(sep); } - rulesetNodes[0].genCSS(env, output); + rulesetNodes[0].genCSS(context, output); for (i = 1; i < rulesetNodeCnt; i++) { if (sep) { output.add(sep); } - rulesetNodes[i].genCSS(env, output); + rulesetNodes[i].genCSS(context, output); } } - if (!output.isEmpty() && !env.compress && this.firstRoot) { + if (!output.isEmpty() && !context.compress && this.firstRoot) { output.add('\n'); } }; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index 993d23709..e2987b6fa 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -53,7 +53,7 @@ Selector.prototype.match = function (other) { Selector.prototype.CacheElements = function() { if (this._elements) return; - + var elements = this.elements.map( function(v) { return v.combinator.value + (v.value.value || v.value); }).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); @@ -65,7 +65,7 @@ Selector.prototype.CacheElements = function() { } else { elements = []; } - + this._elements = elements; }; Selector.prototype.isJustParentSelector = function() { @@ -74,25 +74,25 @@ Selector.prototype.isJustParentSelector = function() { this.elements[0].value === '&' && (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); }; -Selector.prototype.eval = function (env) { - var evaldCondition = this.condition && this.condition.eval(env), +Selector.prototype.eval = function (context) { + var evaldCondition = this.condition && this.condition.eval(context), elements = this.elements, extendList = this.extendList; - elements = elements && elements.map(function (e) { return e.eval(env); }); - extendList = extendList && extendList.map(function(extend) { return extend.eval(env); }); + elements = elements && elements.map(function (e) { return e.eval(context); }); + extendList = extendList && extendList.map(function(extend) { return extend.eval(context); }); return this.createDerived(elements, extendList, evaldCondition); }; -Selector.prototype.genCSS = function (env, output) { +Selector.prototype.genCSS = function (context, output) { var i, element; - if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") { + if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") { output.add(' ', this.currentFileInfo, this.index); } if (!this._css) { //TODO caching? speed comparison? for(i = 0; i < this.elements.length; i++) { element = this.elements[i]; - element.genCSS(env, output); + element.genCSS(context, output); } } }; diff --git a/lib/less/tree/unit.js b/lib/less/tree/unit.js index d9a74598b..2a33bc402 100644 --- a/lib/less/tree/unit.js +++ b/lib/less/tree/unit.js @@ -12,14 +12,14 @@ Unit.prototype.type = "Unit"; Unit.prototype.clone = function () { return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); }; -Unit.prototype.genCSS = function (env, output) { +Unit.prototype.genCSS = function (context, output) { if (this.numerator.length >= 1) { output.add(this.numerator[0]); } else if (this.denominator.length >= 1) { output.add(this.denominator[0]); } else - if ((!env || !env.strictUnits) && this.backupUnit) { + if ((!context || !context.strictUnits) && this.backupUnit) { output.add(this.backupUnit); } }; diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index b528fff71..0c87d0219 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -11,32 +11,32 @@ URL.prototype.type = "Url"; URL.prototype.accept = function (visitor) { this.value = visitor.visit(this.value); }; -URL.prototype.genCSS = function (env, output) { +URL.prototype.genCSS = function (context, output) { output.add("url("); - this.value.genCSS(env, output); + this.value.genCSS(context, output); output.add(")"); }; -URL.prototype.eval = function (ctx) { - var val = this.value.eval(ctx), +URL.prototype.eval = function (context) { + var val = this.value.eval(context), rootpath; if (!this.isEvald) { // Add the base path if the URL is relative rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; - if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) { + if (rootpath && typeof val.value === "string" && context.isPathRelative(val.value)) { if (!val.quote) { rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); } val.value = rootpath + val.value; } - val.value = ctx.normalizePath(val.value); + val.value = context.normalizePath(val.value); // Add url args if enabled - if (ctx.urlArgs) { + if (context.urlArgs) { if (!val.value.match(/^\s*data:/)) { var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; - var urlArgs = delimiter + ctx.urlArgs; + var urlArgs = delimiter + context.urlArgs; if (val.value.indexOf('#') !== -1) { val.value = val.value.replace('#', urlArgs + '#'); } else { diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index b550edd0f..ebdff007f 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -10,21 +10,21 @@ Value.prototype.accept = function (visitor) { this.value = visitor.visitArray(this.value); } }; -Value.prototype.eval = function (env) { +Value.prototype.eval = function (context) { if (this.value.length === 1) { - return this.value[0].eval(env); + return this.value[0].eval(context); } else { return new(Value)(this.value.map(function (v) { - return v.eval(env); + return v.eval(context); })); } }; -Value.prototype.genCSS = function (env, output) { +Value.prototype.genCSS = function (context, output) { var i; for(i = 0; i < this.value.length; i++) { - this.value[i].genCSS(env, output); + this.value[i].genCSS(context, output); if (i+1 < this.value.length) { - output.add((env && env.compress) ? ',' : ', '); + output.add((context && context.compress) ? ',' : ', '); } } }; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 87428af5d..b50db18eb 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -7,11 +7,11 @@ var Variable = function (name, index, currentFileInfo) { }; Variable.prototype = new Node(); Variable.prototype.type = "Variable"; -Variable.prototype.eval = function (env) { +Variable.prototype.eval = function (context) { var variable, name = this.name; if (name.indexOf('@@') === 0) { - name = '@' + new(Variable)(name.slice(1), this.index, this.currentFileInfo).eval(env).value; + name = '@' + new(Variable)(name.slice(1), this.index, this.currentFileInfo).eval(context).value; } if (this.evaluating) { @@ -23,10 +23,10 @@ Variable.prototype.eval = function (env) { this.evaluating = true; - variable = this.find(env.frames, function (frame) { + variable = this.find(context.frames, function (frame) { var v = frame.variable(name); if (v) { - return v.value.eval(env); + return v.value.eval(context); } }); if (variable) { diff --git a/lib/less/visitors/import-visitor.js b/lib/less/visitors/import-visitor.js index 015f3bf53..d74ff0ed5 100644 --- a/lib/less/visitors/import-visitor.js +++ b/lib/less/visitors/import-visitor.js @@ -5,7 +5,7 @@ var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, re this._visitor = new Visitor(this); this._importer = importer; this._finish = finish; - this.env = evalEnv || new contexts.evalEnv(); + this.context = evalEnv || new contexts.Eval(); this.importCount = 0; this.onceFileDetectionMap = onceFileDetectionMap || {}; this.recursionDetector = {}; @@ -44,7 +44,7 @@ ImportVisitor.prototype = { if (!importNode.css || inlineCSS) { try { - evaldImportNode = importNode.evalForImport(this.env); + evaldImportNode = importNode.evalForImport(this.context); } catch(e){ if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } // attempt to eval properly and treat as css @@ -56,10 +56,10 @@ ImportVisitor.prototype = { if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { importNode = evaldImportNode; this.importCount++; - var env = new contexts.evalEnv(this.env, this.env.frames.slice(0)); + var context = new contexts.Eval(this.context, this.context.frames.slice(0)); if (importNode.options.multiple) { - env.importMultiple = true; + context.importMultiple = true; } this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { @@ -68,7 +68,7 @@ ImportVisitor.prototype = { } var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; - if (!env.importMultiple) { + if (!context.importMultiple) { if (duplicateImport) { importNode.skip = true; } else { @@ -94,9 +94,9 @@ ImportVisitor.prototype = { importNode.root = root; importNode.importedFilename = fullPath; - if (!inlineCSS && (env.importMultiple || !duplicateImport)) { + if (!inlineCSS && (context.importMultiple || !duplicateImport)) { importVisitor.recursionDetector[fullPath] = true; - new(ImportVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) + new(ImportVisitor)(importVisitor._importer, subFinish, context, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) .run(root); return; } @@ -114,32 +114,32 @@ ImportVisitor.prototype = { return ruleNode; }, visitDirective: function (directiveNode, visitArgs) { - this.env.frames.unshift(directiveNode); + this.context.frames.unshift(directiveNode); return directiveNode; }, visitDirectiveOut: function (directiveNode) { - this.env.frames.shift(); + this.context.frames.shift(); }, visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { - this.env.frames.unshift(mixinDefinitionNode); + this.context.frames.unshift(mixinDefinitionNode); return mixinDefinitionNode; }, visitMixinDefinitionOut: function (mixinDefinitionNode) { - this.env.frames.shift(); + this.context.frames.shift(); }, visitRuleset: function (rulesetNode, visitArgs) { - this.env.frames.unshift(rulesetNode); + this.context.frames.unshift(rulesetNode); return rulesetNode; }, visitRulesetOut: function (rulesetNode) { - this.env.frames.shift(); + this.context.frames.shift(); }, visitMedia: function (mediaNode, visitArgs) { - this.env.frames.unshift(mediaNode.rules[0]); + this.context.frames.unshift(mediaNode.rules[0]); return mediaNode; }, visitMediaOut: function (mediaNode) { - this.env.frames.shift(); + this.context.frames.shift(); } }; module.exports = ImportVisitor; diff --git a/lib/less/visitors/to-css-visitor.js b/lib/less/visitors/to-css-visitor.js index e9e19d127..696589884 100644 --- a/lib/less/visitors/to-css-visitor.js +++ b/lib/less/visitors/to-css-visitor.js @@ -1,9 +1,9 @@ var tree = require("../tree"), Visitor = require("./visitor"); -var ToCSSVisitor = function(env) { +var ToCSSVisitor = function(context) { this._visitor = new Visitor(this); - this._env = env; + this._context = context; }; ToCSSVisitor.prototype = { @@ -31,7 +31,7 @@ ToCSSVisitor.prototype = { }, visitComment: function (commentNode, visitArgs) { - if (commentNode.isSilent(this._env)) { + if (commentNode.isSilent(this._context)) { return []; } return commentNode; @@ -57,7 +57,7 @@ ToCSSVisitor.prototype = { // be considered illegal css as it has to be on the first line if (this.charset) { if (directiveNode.debugInfo) { - var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n"); + var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "")+" */\n"); comment.debugInfo = directiveNode.debugInfo; return this._visitor.visit(comment); } @@ -168,9 +168,9 @@ ToCSSVisitor.prototype = { } else { ruleList = ruleCache[rule.name]; if (ruleList instanceof tree.Rule) { - ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)]; + ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)]; } - var ruleCSS = rule.toCSS(this._env); + var ruleCSS = rule.toCSS(this._context); if (ruleList.indexOf(ruleCSS) !== -1) { rules.splice(i, 1); } else { From 8d431179e57c713d770650bd246c8ac5fac3d2d2 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 5 Oct 2014 20:36:23 +0100 Subject: [PATCH 116/161] Add logger --- bin/lessc | 30 +++++++++---- lib/less-browser/browser-import.js | 6 +-- lib/less-browser/index.js | 67 ++++++++++++++++++------------ lib/less-node/environment.js | 3 -- lib/less-node/url-import.js | 5 ++- lib/less/contexts.js | 2 - lib/less/functions/data-uri.js | 7 ++-- lib/less/index.js | 3 +- lib/less/logger.js | 34 +++++++++++++++ 9 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 lib/less/logger.js diff --git a/bin/lessc b/bin/lessc index afb75c3d1..9a497bdc1 100755 --- a/bin/lessc +++ b/bin/lessc @@ -9,12 +9,12 @@ var less = require('../lib/less-node'), pluginManager = new less.PluginManager(less); var args = process.argv.slice(1); -var options = { +var silent = false, + verbose = false, + options = { depends: false, compress: false, max_line_len: -1, - silent: false, - verbose: false, lint: false, paths: [], color: true, @@ -90,11 +90,11 @@ args = args.filter(function (arg) { continueProcessing = false; break; case 'verbose': - options.verbose = true; + verbose = true; break; case 's': case 'silent': - options.silent = true; + silent = true; break; case 'l': case 'lint': @@ -364,15 +364,29 @@ var parseLessFile = function (e, data) { options.plugins = pluginManager; + less.logger.addListener({ + info: function(msg) { + if (verbose) { + console.log(msg); + } + }, + warn: function(msg) { + if (!silent) { + console.warn(msg); + } + }, + error: function(msg) { + console.log(msg); + } + }); + less.render(data, options) .then(function(result) { if(!options.lint) { if (output) { ensureDirectory(output); fs.writeFileSync(output, result.css, 'utf8'); - if (options.verbose) { - console.log('lessc: wrote ' + output); - } + less.logger.info('lessc: wrote ' + output); } else { process.stdout.write(result.css); } diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js index 8e4b4a0c8..e5279c026 100644 --- a/lib/less-browser/browser-import.js +++ b/lib/less-browser/browser-import.js @@ -1,6 +1,6 @@ /*global window, XMLHttpRequest */ -module.exports = function(options, isFileProtocol, log, logLevel) { +module.exports = function(options, isFileProtocol, logger) { var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, AbstractFileManager = require("../less/environment/abstract-file-manager.js"); @@ -18,7 +18,7 @@ function getXMLHttpRequest() { /*global ActiveXObject */ return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { - log("browser doesn't support AJAX.", logLevel.errors); + logger.error("browser doesn't support AJAX."); return null; } } @@ -46,7 +46,7 @@ BrowserImport.prototype.doXHR = function doXHR(url, type, callback, errback) { if (typeof(xhr.overrideMimeType) === 'function') { xhr.overrideMimeType('text/css'); } - log("XHR: Getting '" + url + "'", logLevel.debug); + logger.debug("XHR: Getting '" + url + "'"); xhr.open('GET', url, async); xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); xhr.send(null); diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 2d48326d7..e75f067a5 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -3,23 +3,8 @@ // /*global window, document, location */ -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; - var less; -function log(str, level) { - if (typeof(console) !== 'undefined' && less.logLevel >= level) { - //TODO no idea why console.log has stopped working in phantom - //move to a proper logger and registering a listener - console.warn('less: ' + str); - } -} - /* TODO - options is now hidden - we should expose it on the less object, but not have it "as" the less object alternately even have it on environment @@ -30,11 +15,41 @@ function log(str, level) { */ var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), - options = window.less || {}, - environment = require("./environment"), - browserImport = require("./browser-import")(options, isFileProtocol, log, logLevel); + options = window.less || {}; -window.less = less = require('../less')(environment, [browserImport]); +window.less = less = require('../less')(require("./environment")); +var environment = less.environment, + browserImport = require("./browser-import")(options, isFileProtocol, less.logger); +environment.addFileManager(browserImport); + +var logLevel = { + debug: 3, + info: 2, + errors: 1, + none: 0 +}; +less.logger.addListener({ + debug: function(msg) { + if (options.logLevel >= logLevel.debug) { + console.log(msg); + } + }, + info: function(msg) { + if (options.logLevel >= logLevel.info) { + console.log(msg); + } + }, + warn: function(msg) { + if (options.logLevel >= logLevel.warn) { + console.warn(msg); + } + }, + error: function(msg) { + if (options.logLevel >= logLevel.error) { + console.error(msg); + } + } +}); less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || @@ -113,7 +128,7 @@ function errorConsole(e, rootHref) { } else if (e.stack) { content += e.stack; } - log(content, logLevel.errors); + less.logger.error(content); } function createCSS(styles, sheet, lastModified) { @@ -172,13 +187,13 @@ function createCSS(styles, sheet, lastModified) { // Don't update the local store if the file wasn't modified if (lastModified && cache) { - log('saving ' + href + ' to cache.', logLevel.info); + less.logger.info('saving ' + href + ' to cache.'); try { cache.setItem(href, styles); cache.setItem(href + ':timestamp', lastModified); } catch(e) { //TODO - could do with adding more robust error handling - log('failed to save', logLevel.errors); + less.logger.error('failed to save'); } } } @@ -513,15 +528,15 @@ less.refresh = function (reload, modifyVars) { return error(e, e.href || sheet.href); } if (webInfo.local) { - log("loading " + sheet.href + " from cache.", logLevel.info); + less.logger.info("loading " + sheet.href + " from cache."); } else { - log("rendered " + sheet.href + " successfully.", logLevel.debug); + less.logger.info("rendered " + sheet.href + " successfully."); css = postProcessCSS(css); createCSS(css, sheet, webInfo.lastModified); } - log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info); + less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); if (webInfo.remaining === 0) { - log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info); + less.logger.info("less has finished. css generated in " + (new Date() - startTime) + 'ms'); } endTime = new Date(); }, reload, modifyVars); diff --git a/lib/less-node/environment.js b/lib/less-node/environment.js index 63b564b51..4df4e1851 100644 --- a/lib/less-node/environment.js +++ b/lib/less-node/environment.js @@ -1,7 +1,4 @@ module.exports = { - warn: function(msg) { - console.warn(msg); - }, encodeBase64: function encodeBase64(str) { return new Buffer(str).toString('base64'); }, diff --git a/lib/less-node/url-import.js b/lib/less-node/url-import.js index 7d5aa46b9..63a20416b 100644 --- a/lib/less-node/url-import.js +++ b/lib/less-node/url-import.js @@ -2,7 +2,8 @@ var isUrlRe = /^(?:https?:)?\/\//i, url = require('url'), request, PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, - AbstractFileManager = require("../less/environment/abstract-file-manager.js"); + AbstractFileManager = require("../less/environment/abstract-file-manager.js"), + logger = require("../less/logger"); var UrlImport = function() { }; @@ -42,7 +43,7 @@ UrlImport.prototype.loadFile = function(filename, currentDirectory, options, env return; } if (!body) { - environment.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); + logger.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"'); } fullfill({ contents: body, filename: urlStr }); }); diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 84da3e60d..ca3405137 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -40,8 +40,6 @@ contexts.Parse = function(options) { }; var evalCopyProperties = [ - 'silent', // whether to swallow errors and warnings - 'verbose', // whether to log more activity 'compress', // whether to compress 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) 'strictMath', // whether math has to be within parenthesis diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js index 205dfb465..dc7b97763 100644 --- a/lib/less/functions/data-uri.js +++ b/lib/less/functions/data-uri.js @@ -4,7 +4,8 @@ module.exports = function(environment) { functionRegistry = require("./function-registry"), fallback = function(functionThis, node) { return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context); - }; + }, + logger = require('../logger'); functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { @@ -49,7 +50,7 @@ module.exports = function(environment) { var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment); if (!fileSync.contents) { - environment.warn("Skipped data-uri embedding because file not found"); + logger.warn("Skipped data-uri embedding because file not found"); return fallback(this, filePathNode || mimetypeNode); } var buf = fileSync.contents; @@ -61,7 +62,7 @@ module.exports = function(environment) { if (fileSizeInKB >= DATA_URI_MAX_KB) { if (this.context.ieCompat !== false) { - environment.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); + logger.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); return fallback(this, filePathNode || mimetypeNode); } diff --git a/lib/less/index.js b/lib/less/index.js index b2be18b53..43546e062 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -20,7 +20,8 @@ module.exports = function(environment, fileManagers) { LessError: require('./less-error'), transformTree: require('./transform-tree'), utils: require('./utils'), - PluginManager: require('./plugin-manager') + PluginManager: require('./plugin-manager'), + logger: require('./logger') }; return less; diff --git a/lib/less/logger.js b/lib/less/logger.js new file mode 100644 index 000000000..d073a0108 --- /dev/null +++ b/lib/less/logger.js @@ -0,0 +1,34 @@ +module.exports = { + error: function(msg) { + this._fireEvent("error", msg); + }, + warn: function(msg) { + this._fireEvent("warn", msg); + }, + info: function(msg) { + this._fireEvent("info", msg); + }, + debug: function(msg) { + this._fireEvent("debug", msg); + }, + addListener: function(listener) { + this._listeners.push(listener); + }, + removeListener: function(listener) { + for(var i = 0; i < this._listeners.length; i++) { + if (this._listeners[i] === listener) { + this._listeners.splice(i, 1); + return; + } + } + }, + _fireEvent: function(type, msg) { + for(var i = 0; i < this._listeners.length; i++) { + var logFunction = this._listeners[i]["type"]; + if (logFunction) { + logFunction(msg); + } + } + }, + _listeners: [] +}; From 9be165ba8a6b5e7e0ad1bd520f60f978da53aeac Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 7 Oct 2014 07:42:35 +0100 Subject: [PATCH 117/161] Fix tests by adding logger to record messages in test common js --- lib/less-browser/index.js | 47 ++++++++++++++++++++++----------------- lib/less/logger.js | 2 +- test/browser/common.js | 17 +++++++------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index e75f067a5..86e5ec9e0 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -28,28 +28,33 @@ var logLevel = { errors: 1, none: 0 }; -less.logger.addListener({ - debug: function(msg) { - if (options.logLevel >= logLevel.debug) { - console.log(msg); - } - }, - info: function(msg) { - if (options.logLevel >= logLevel.info) { - console.log(msg); - } - }, - warn: function(msg) { - if (options.logLevel >= logLevel.warn) { - console.warn(msg); - } - }, - error: function(msg) { - if (options.logLevel >= logLevel.error) { - console.error(msg); +if (!options.loggers) { + options.loggers = [{ + debug: function(msg) { + if (options.logLevel >= logLevel.debug) { + console.log(msg); + } + }, + info: function(msg) { + if (options.logLevel >= logLevel.info) { + console.log(msg); + } + }, + warn: function(msg) { + if (options.logLevel >= logLevel.warn) { + console.warn(msg); + } + }, + error: function(msg) { + if (options.logLevel >= logLevel.error) { + console.error(msg); + } } - } -}); + }]; +} +for(var i = 0; i < options.loggers.length; i++) { + less.logger.addListener(options.loggers[i]); +} less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || diff --git a/lib/less/logger.js b/lib/less/logger.js index d073a0108..352274d9b 100644 --- a/lib/less/logger.js +++ b/lib/less/logger.js @@ -24,7 +24,7 @@ module.exports = { }, _fireEvent: function(type, msg) { for(var i = 0; i < this._listeners.length; i++) { - var logFunction = this._listeners[i]["type"]; + var logFunction = this._listeners[i][type]; if (logFunction) { logFunction(msg); } diff --git a/test/browser/common.js b/test/browser/common.js index 09e2e5455..40ec8eb99 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -10,21 +10,22 @@ // } // }; -var oldconsole = window.console; var logMessages = []; -window.console = { - warn: function(msg) { - oldconsole.warn(msg); +window.less = window.less || {}; +less.loggers = [{ + info: function(msg) { + logMessages.push(msg); + }, + debug: function(msg) { logMessages.push(msg); }, - log: function(msg) { - oldconsole.log(msg); + warn: function(msg) { logMessages.push(msg); }, error: function(msg) { - oldconsole.error(msg); + logMessages.push(msg); } -}; +}]; var testLessEqualsInDocument = function() { testLessInDocument(testSheet); From 91025dbe7e2f184a869fa4e55fd1b1f15c535fe9 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 7 Oct 2014 21:05:26 +0100 Subject: [PATCH 118/161] move browser log code out to log listener and remove option I added temporarily that wasn't an opiption --- lib/less-browser/index.js | 49 +------------------------------- lib/less-browser/log-listener.js | 43 ++++++++++++++++++++++++++++ lib/less/contexts.js | 3 +- 3 files changed, 45 insertions(+), 50 deletions(-) create mode 100644 lib/less-browser/log-listener.js diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 86e5ec9e0..4ddb66b7f 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -7,9 +7,6 @@ var less; /* TODO - options is now hidden - we should expose it on the less object, but not have it "as" the less object - alternately even have it on environment - e.g. less.environment.options.fileAsync = true; - is it weird you do less = { fileAsync: true } then access as less.environment.options.fileAsync ? */ @@ -22,39 +19,7 @@ var environment = less.environment, browserImport = require("./browser-import")(options, isFileProtocol, less.logger); environment.addFileManager(browserImport); -var logLevel = { - debug: 3, - info: 2, - errors: 1, - none: 0 -}; -if (!options.loggers) { - options.loggers = [{ - debug: function(msg) { - if (options.logLevel >= logLevel.debug) { - console.log(msg); - } - }, - info: function(msg) { - if (options.logLevel >= logLevel.info) { - console.log(msg); - } - }, - warn: function(msg) { - if (options.logLevel >= logLevel.warn) { - console.warn(msg); - } - }, - error: function(msg) { - if (options.logLevel >= logLevel.error) { - console.error(msg); - } - } - }]; -} -for(var i = 0; i < options.loggers.length; i++) { - less.logger.addListener(options.loggers[i]); -} +require("./log-listener")(less, options); less.env = options.env || (location.hostname == '127.0.0.1' || location.hostname == '0.0.0.0' || @@ -64,18 +29,6 @@ less.env = options.env || (location.hostname == '127.0.0.1' || isFileProtocol ? 'development' : 'production'); -if (!options.hasOwnProperty('disableDataURIs')) { - options.disableDataURIs = true; -} - -// The amount of logging in the javascript console. -// 3 - Debug, information and errors -// 2 - Information and errors -// 1 - Errors -// 0 - None -// Defaults to 2 -less.logLevel = typeof(options.logLevel) != 'undefined' ? options.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors); - // Load styles asynchronously (default: false) // // This is set to `false` by default, so that the body diff --git a/lib/less-browser/log-listener.js b/lib/less-browser/log-listener.js new file mode 100644 index 000000000..1b1ad59a7 --- /dev/null +++ b/lib/less-browser/log-listener.js @@ -0,0 +1,43 @@ +module.exports = function(less, options) { + + var logLevel_debug = 3, + logLevel_info = 2, + logLevel_errors = 1, + logLevel_none = 0; + + // The amount of logging in the javascript console. + // 3 - Debug, information and errors + // 2 - Information and errors + // 1 - Errors + // 0 - None + // Defaults to 2 + options.logLevel = typeof(options.logLevel) !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_debug : logLevel_errors); + + if (!options.loggers) { + options.loggers = [{ + debug: function(msg) { + if (options.logLevel >= logLevel_debug) { + console.log(msg); + } + }, + info: function(msg) { + if (options.logLevel >= logLevel_info) { + console.log(msg); + } + }, + warn: function(msg) { + if (options.logLevel >= logLevel_warn) { + console.warn(msg); + } + }, + error: function(msg) { + if (options.logLevel >= logLevel_error) { + console.error(msg); + } + } + }]; + } + for(var i = 0; i < options.loggers.length; i++) { + less.logger.addListener(options.loggers[i]); + } +}; diff --git a/lib/less/contexts.js b/lib/less/contexts.js index ca3405137..96335a7a9 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -47,8 +47,7 @@ var evalCopyProperties = [ 'sourceMap', // whether to output a source map 'importMultiple', // whether we are currently importing multiple copies 'urlArgs', // whether to add args into url tokens - 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true - 'disableDataURIs' // option - disable data URIs (they will be converted to URL()) + 'javascriptEnabled'// option - whether JavaScript is enabled. if undefined, defaults to true ]; contexts.Eval = function(options, frames) { From cd0cf0e24353d6de9d687e146a2293d0fbc36a34 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 7 Oct 2014 21:47:35 +0100 Subject: [PATCH 119/161] Seperate browser a bit more --- Gruntfile.js | 3 + lib/less-browser/browser.js | 57 ++++++ lib/less-browser/error-reporting.js | 168 +++++++++++++++++ lib/less-browser/index.js | 276 ++++------------------------ lib/less-browser/log-listener.js | 10 +- lib/less-browser/utils.js | 9 + 6 files changed, 273 insertions(+), 250 deletions(-) create mode 100644 lib/less-browser/browser.js create mode 100644 lib/less-browser/error-reporting.js create mode 100644 lib/less-browser/utils.js diff --git a/Gruntfile.js b/Gruntfile.js index 37275a716..b52fef8df 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -103,6 +103,9 @@ module.exports = function (grunt) { src: [ 'Gruntfile.js', 'lib/less/**/*.js', + 'lib/less-node/**/*.js', + 'lib/less-browser/**/*.js', + 'lib/less-rhino/**/*.js', 'bin/lessc' ] } diff --git a/lib/less-browser/browser.js b/lib/less-browser/browser.js new file mode 100644 index 000000000..09eb7b9d2 --- /dev/null +++ b/lib/less-browser/browser.js @@ -0,0 +1,57 @@ +var utils = require("./utils"); +module.exports = { + createCSS: function (document, styles, sheet, lastModified) { + // Strip the query-string + var href = sheet.href || ''; + + // If there is no title set, use the filename, minus the extension + var id = 'less:' + (sheet.title || utils.extractId(href)); + + // If this has already been inserted into the DOM, we may need to replace it + var oldCss = document.getElementById(id); + var keepOldCss = false; + + // Create a new stylesheet node for insertion or (if necessary) replacement + var css = document.createElement('style'); + css.setAttribute('type', 'text/css'); + if (sheet.media) { + css.setAttribute('media', sheet.media); + } + css.id = id; + + if (!css.styleSheet) { + css.appendChild(document.createTextNode(styles)); + + // If new contents match contents of oldCss, don't replace oldCss + keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && + oldCss.firstChild.nodeValue === css.firstChild.nodeValue); + } + + var head = document.getElementsByTagName('head')[0]; + + // If there is no oldCss, just append; otherwise, only append if we need + // to replace oldCss with an updated stylesheet + if (oldCss === null || keepOldCss === false) { + var nextEl = sheet && sheet.nextSibling || null; + if (nextEl) { + nextEl.parentNode.insertBefore(css, nextEl); + } else { + head.appendChild(css); + } + } + if (oldCss && keepOldCss === false) { + oldCss.parentNode.removeChild(oldCss); + } + + // For IE. + // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. + // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head + if (css.styleSheet) { + try { + css.styleSheet.cssText = styles; + } catch (e) { + throw new(Error)("Couldn't reassign styleSheet.cssText."); + } + } + } +}; diff --git a/lib/less-browser/error-reporting.js b/lib/less-browser/error-reporting.js new file mode 100644 index 000000000..54d23c26f --- /dev/null +++ b/lib/less-browser/error-reporting.js @@ -0,0 +1,168 @@ +var utils = require("./utils"), + browser = require("./browser"); + +module.exports = function(window, less, options) { + + function errorHTML(e, rootHref) { + var id = 'less-error-message:' + utils.extractId(rootHref || ""); + var template = '
  • {content}
  • '; + var elem = window.document.createElement('div'), timer, content, errors = []; + var filename = e.filename || rootHref; + var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; + + elem.id = id; + elem.className = "less-error-message"; + + content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + + '

    ' + '

    in ' + filenameNoPath + " "; + + var errorline = function (e, i, classname) { + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) + .replace(/\{class\}/, classname) + .replace(/\{content\}/, e.extract[i])); + } + }; + + if (e.extract) { + errorline(e, 0, ''); + errorline(e, 1, 'line'); + errorline(e, 2, ''); + content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + + '
      ' + errors.join('') + '
    '; + } else if (e.stack) { + content += '
    ' + e.stack.split('\n').slice(1).join('
    '); + } + elem.innerHTML = content; + + // CSS for error messages + browser.createCSS(window.document, [ + '.less-error-message ul, .less-error-message li {', + 'list-style-type: none;', + 'margin-right: 15px;', + 'padding: 4px 0;', + 'margin: 0;', + '}', + '.less-error-message label {', + 'font-size: 12px;', + 'margin-right: 15px;', + 'padding: 4px 0;', + 'color: #cc7777;', + '}', + '.less-error-message pre {', + 'color: #dd6666;', + 'padding: 4px 0;', + 'margin: 0;', + 'display: inline-block;', + '}', + '.less-error-message pre.line {', + 'color: #ff0000;', + '}', + '.less-error-message h3 {', + 'font-size: 20px;', + 'font-weight: bold;', + 'padding: 15px 0 5px 0;', + 'margin: 0;', + '}', + '.less-error-message a {', + 'color: #10a', + '}', + '.less-error-message .error {', + 'color: red;', + 'font-weight: bold;', + 'padding-bottom: 2px;', + 'border-bottom: 1px dashed red;', + '}' + ].join('\n'), { title: 'error-message' }); + + elem.style.cssText = [ + "font-family: Arial, sans-serif", + "border: 1px solid #e00", + "background-color: #eee", + "border-radius: 5px", + "-webkit-border-radius: 5px", + "-moz-border-radius: 5px", + "color: #e00", + "padding: 15px", + "margin-bottom: 15px" + ].join(';'); + + if (options.env === 'development') { + timer = setInterval(function () { + var document = window.document, + body = document.body; + if (body) { + if (document.getElementById(id)) { + body.replaceChild(elem, document.getElementById(id)); + } else { + body.insertBefore(elem, body.firstChild); + } + clearInterval(timer); + } + }, 10); + } + } + + function error(e, rootHref) { + if (!options.errorReporting || options.errorReporting === "html") { + errorHTML(e, rootHref); + } else if (options.errorReporting === "console") { + errorConsole(e, rootHref); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("add", e, rootHref); + } + } + + function removeErrorHTML(path) { + var node = window.document.getElementById('less-error-message:' + utils.extractId(path)); + if (node) { + node.parentNode.removeChild(node); + } + } + + function removeErrorConsole(path) { + //no action + } + + function removeError(path) { + if (!options.errorReporting || options.errorReporting === "html") { + removeErrorHTML(path); + } else if (options.errorReporting === "console") { + removeErrorConsole(path); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("remove", path); + } + } + + function errorConsole(e, rootHref) { + var template = '{line} {content}'; + var filename = e.filename || rootHref; + var errors = []; + var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + + " in " + filename + " "; + + var errorline = function (e, i, classname) { + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) + .replace(/\{class\}/, classname) + .replace(/\{content\}/, e.extract[i])); + } + }; + + if (e.extract) { + errorline(e, 0, ''); + errorline(e, 1, 'line'); + errorline(e, 2, ''); + content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + + errors.join('\n'); + } else if (e.stack) { + content += e.stack; + } + less.logger.error(content); + } + + return { + add: error, + remove: removeError + }; +}; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 4ddb66b7f..3494fb09c 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -11,7 +11,7 @@ var less; then access as less.environment.options.fileAsync ? */ -var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol), +var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol), options = window.less || {}; window.less = less = require('../less')(require("./environment")); @@ -20,12 +20,15 @@ var environment = less.environment, environment.addFileManager(browserImport); require("./log-listener")(less, options); - -less.env = options.env || (location.hostname == '127.0.0.1' || - location.hostname == '0.0.0.0' || - location.hostname == 'localhost' || - (location.port && - location.port.length > 0) || +//var utils = require("./utils"); +var errors = require("./error-reporting")(window, less, options); +var browser = require("./browser"); + +options.env = options.env || (window.location.hostname == '127.0.0.1' || + window.location.hostname == '0.0.0.0' || + window.location.hostname == 'localhost' || + (window.location.port && + window.location.port.length > 0) || isFileProtocol ? 'development' : 'production'); @@ -39,7 +42,7 @@ options.async = options.async || false; options.fileAsync = options.fileAsync || false; // Interval between watch polls -less.poll = less.poll || (isFileProtocol ? 1000 : 1500); +options.poll = options.poll || (isFileProtocol ? 1000 : 1500); //Setup user functions if (options.functions) { @@ -48,114 +51,12 @@ if (options.functions) { var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); if (dumpLineNumbers) { - less.dumpLineNumbers = dumpLineNumbers[1]; + options.dumpLineNumbers = dumpLineNumbers[1]; } var typePattern = /^text\/(x-)?less$/; var cache = null; -function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain - .replace(/^\//, '' ) // Remove root / - .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) -} - -function errorConsole(e, rootHref) { - var template = '{line} {content}'; - var filename = e.filename || rootHref; - var errors = []; - var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - " in " + filename + " "; - - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + - errors.join('\n'); - } else if (e.stack) { - content += e.stack; - } - less.logger.error(content); -} - -function createCSS(styles, sheet, lastModified) { - // Strip the query-string - var href = sheet.href || ''; - - // If there is no title set, use the filename, minus the extension - var id = 'less:' + (sheet.title || extractId(href)); - - // If this has already been inserted into the DOM, we may need to replace it - var oldCss = document.getElementById(id); - var keepOldCss = false; - - // Create a new stylesheet node for insertion or (if necessary) replacement - var css = document.createElement('style'); - css.setAttribute('type', 'text/css'); - if (sheet.media) { - css.setAttribute('media', sheet.media); - } - css.id = id; - - if (!css.styleSheet) { - css.appendChild(document.createTextNode(styles)); - - // If new contents match contents of oldCss, don't replace oldCss - keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 && - oldCss.firstChild.nodeValue === css.firstChild.nodeValue); - } - - var head = document.getElementsByTagName('head')[0]; - - // If there is no oldCss, just append; otherwise, only append if we need - // to replace oldCss with an updated stylesheet - if (oldCss === null || keepOldCss === false) { - var nextEl = sheet && sheet.nextSibling || null; - if (nextEl) { - nextEl.parentNode.insertBefore(css, nextEl); - } else { - head.appendChild(css); - } - } - if (oldCss && keepOldCss === false) { - oldCss.parentNode.removeChild(oldCss); - } - - // For IE. - // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. - // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head - if (css.styleSheet) { - try { - css.styleSheet.cssText = styles; - } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); - } - } - - // Don't update the local store if the file wasn't modified - if (lastModified && cache) { - less.logger.info('saving ' + href + ' to cache.'); - try { - cache.setItem(href, styles); - cache.setItem(href + ':timestamp', lastModified); - } catch(e) { - //TODO - could do with adding more robust error handling - less.logger.error('failed to save'); - } - } -} - function postProcessCSS(styles) { if (options.postProcessor && typeof options.postProcessor === 'function') { styles = options.postProcessor.call(styles, styles) || styles; @@ -163,134 +64,19 @@ function postProcessCSS(styles) { return styles; } -function errorHTML(e, rootHref) { - var id = 'less-error-message:' + extractId(rootHref || ""); - var template = '
  • {content}
  • '; - var elem = document.createElement('div'), timer, content, errors = []; - var filename = e.filename || rootHref; - var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; - - elem.id = id; - elem.className = "less-error-message"; - - content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + - '

    ' + '

    in ' + filenameNoPath + " "; +// Replace the cache system +// Don't update the local store if the file wasn't modified +//if (lastModified && cache) { +// logger.info('saving ' + href + ' to cache.'); +// try { +// cache.setItem(href, styles); +// cache.setItem(href + ':timestamp', lastModified); +// } catch(e) { +// //TODO - could do with adding more robust error handling +// logger.error('failed to save'); +// }// +//} - var errorline = function (e, i, classname) { - if (e.extract[i] !== undefined) { - errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) - .replace(/\{class\}/, classname) - .replace(/\{content\}/, e.extract[i])); - } - }; - - if (e.extract) { - errorline(e, 0, ''); - errorline(e, 1, 'line'); - errorline(e, 2, ''); - content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + - '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); - } - elem.innerHTML = content; - - // CSS for error messages - createCSS([ - '.less-error-message ul, .less-error-message li {', - 'list-style-type: none;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'margin: 0;', - '}', - '.less-error-message label {', - 'font-size: 12px;', - 'margin-right: 15px;', - 'padding: 4px 0;', - 'color: #cc7777;', - '}', - '.less-error-message pre {', - 'color: #dd6666;', - 'padding: 4px 0;', - 'margin: 0;', - 'display: inline-block;', - '}', - '.less-error-message pre.line {', - 'color: #ff0000;', - '}', - '.less-error-message h3 {', - 'font-size: 20px;', - 'font-weight: bold;', - 'padding: 15px 0 5px 0;', - 'margin: 0;', - '}', - '.less-error-message a {', - 'color: #10a', - '}', - '.less-error-message .error {', - 'color: red;', - 'font-weight: bold;', - 'padding-bottom: 2px;', - 'border-bottom: 1px dashed red;', - '}' - ].join('\n'), { title: 'error-message' }); - - elem.style.cssText = [ - "font-family: Arial, sans-serif", - "border: 1px solid #e00", - "background-color: #eee", - "border-radius: 5px", - "-webkit-border-radius: 5px", - "-moz-border-radius: 5px", - "color: #e00", - "padding: 15px", - "margin-bottom: 15px" - ].join(';'); - - if (less.env == 'development') { - timer = setInterval(function () { - if (document.body) { - if (document.getElementById(id)) { - document.body.replaceChild(elem, document.getElementById(id)); - } else { - document.body.insertBefore(elem, document.body.firstChild); - } - clearInterval(timer); - } - }, 10); - } -} - -function error(e, rootHref) { - if (!options.errorReporting || options.errorReporting === "html") { - errorHTML(e, rootHref); - } else if (options.errorReporting === "console") { - errorConsole(e, rootHref); - } else if (typeof options.errorReporting === 'function') { - options.errorReporting("add", e, rootHref); - } -} - -function removeErrorHTML(path) { - var node = document.getElementById('less-error-message:' + extractId(path)); - if (node) { - node.parentNode.removeChild(node); - } -} - -function removeErrorConsole(path) { - //no action -} - -function removeError(path) { - if (!options.errorReporting || options.errorReporting === "html") { - removeErrorHTML(path); - } else if (options.errorReporting === "console") { - removeErrorConsole(path); - } else if (typeof options.errorReporting === 'function') { - options.errorReporting("remove", path); - } -} function clone(obj) { var cloned = {}; @@ -339,7 +125,7 @@ function loadStyles(modifyVars) { } }, null, style), function(e) { - error(e, "inline"); + errors.add(e, "inline"); }); } } @@ -383,7 +169,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { (new(Date)(webInfo.lastModified).valueOf() === new(Date)(timestamp).valueOf())) { // Use local copy - createCSS(css, sheet); + browser.createCSS(window.document, css, sheet); webInfo.local = true; callback(null, null, data, sheet, webInfo, path); return; @@ -391,7 +177,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { } //TODO add tests around how this behaves when reloading - removeError(path); + errors.remove(path); instanceOptions.rootFileInfo = newFileInfo; less.render(data, instanceOptions) @@ -420,10 +206,10 @@ function initRunningMode(){ if (less.watchMode) { loadStyleSheets(function (e, css, _, sheet, context) { if (e) { - error(e, e.href || sheet.href); + errors.add(e, e.href || sheet.href); } else if (css) { css = postProcessCSS(css); - createCSS(css, sheet, context.lastModified); + browser.createCSS(window.document, css, sheet, context.lastModified); } }); } @@ -483,14 +269,14 @@ less.refresh = function (reload, modifyVars) { loadStyleSheets(function (e, css, _, sheet, webInfo) { if (e) { - return error(e, e.href || sheet.href); + return errors.add(e, e.href || sheet.href); } if (webInfo.local) { less.logger.info("loading " + sheet.href + " from cache."); } else { less.logger.info("rendered " + sheet.href + " successfully."); css = postProcessCSS(css); - createCSS(css, sheet, webInfo.lastModified); + browser.createCSS(window.document, css, sheet, webInfo.lastModified); } less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); if (webInfo.remaining === 0) { diff --git a/lib/less-browser/log-listener.js b/lib/less-browser/log-listener.js index 1b1ad59a7..9806c9965 100644 --- a/lib/less-browser/log-listener.js +++ b/lib/less-browser/log-listener.js @@ -1,9 +1,9 @@ module.exports = function(less, options) { - var logLevel_debug = 3, - logLevel_info = 2, - logLevel_errors = 1, - logLevel_none = 0; + var logLevel_debug = 4, + logLevel_info = 3, + logLevel_warn = 2, + logLevel_error = 1; // The amount of logging in the javascript console. // 3 - Debug, information and errors @@ -11,7 +11,7 @@ module.exports = function(less, options) { // 1 - Errors // 0 - None // Defaults to 2 - options.logLevel = typeof(options.logLevel) !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_debug : logLevel_errors); + options.logLevel = typeof(options.logLevel) !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_debug : logLevel_error); if (!options.loggers) { options.loggers = [{ diff --git a/lib/less-browser/utils.js b/lib/less-browser/utils.js new file mode 100644 index 000000000..8d6644fc5 --- /dev/null +++ b/lib/less-browser/utils.js @@ -0,0 +1,9 @@ +module.exports = { + extractId: function(href) { + return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain + .replace(/^\//, '' ) // Remove root / + .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension + .replace(/[^\.\w-]+/g, '-') // Replace illegal characters + .replace(/\./g, ':'); // Replace dots with colons(for valid id) + } +}; From 3450e336b8ac4d5157f24d88e53a78885bd35c8f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Tue, 7 Oct 2014 23:20:47 +0100 Subject: [PATCH 120/161] Seperate out the browser cache --- lib/less-browser/browser.js | 2 +- lib/less-browser/cache.js | 35 +++++++++++++++++++++++++++++++++++ lib/less-browser/index.js | 37 +++++++------------------------------ 3 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 lib/less-browser/cache.js diff --git a/lib/less-browser/browser.js b/lib/less-browser/browser.js index 09eb7b9d2..c289a1551 100644 --- a/lib/less-browser/browser.js +++ b/lib/less-browser/browser.js @@ -1,6 +1,6 @@ var utils = require("./utils"); module.exports = { - createCSS: function (document, styles, sheet, lastModified) { + createCSS: function (document, styles, sheet) { // Strip the query-string var href = sheet.href || ''; diff --git a/lib/less-browser/cache.js b/lib/less-browser/cache.js new file mode 100644 index 000000000..fd7a45497 --- /dev/null +++ b/lib/less-browser/cache.js @@ -0,0 +1,35 @@ +// Cache system is a bit outdated and could do with work + +module.exports = function(window, options, logger) { + var cache = null; + if (options.env !== 'development') { + try { + cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; + } catch (_) {} + } + return { + setCSS: function(path, lastModified, styles) { + if (cache) { + logger.info('saving ' + path+ ' to cache.'); + try { + cache.setItem(path, styles); + cache.setItem(path + ':timestamp', lastModified); + } catch(e) { + //TODO - could do with adding more robust error handling + logger.error('failed to save'); + } + } + }, + getCSS: function(path, webInfo) { + var css = cache && cache.getItem(path), + timestamp = cache && cache.getItem(path + ':timestamp'); + + if (timestamp && webInfo.lastModified && + (new(Date)(webInfo.lastModified).valueOf() === + new(Date)(timestamp).valueOf())) { + // Use local copy + return css; + } + } + }; +}; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 3494fb09c..801c5bd46 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -23,6 +23,7 @@ require("./log-listener")(less, options); //var utils = require("./utils"); var errors = require("./error-reporting")(window, less, options); var browser = require("./browser"); +var cache = options.cache || require("./cache")(window, options, less.logger); options.env = options.env || (window.location.hostname == '127.0.0.1' || window.location.hostname == '0.0.0.0' || @@ -55,7 +56,6 @@ if (dumpLineNumbers) { } var typePattern = /^text\/(x-)?less$/; -var cache = null; function postProcessCSS(styles) { if (options.postProcessor && typeof options.postProcessor === 'function') { @@ -64,20 +64,6 @@ function postProcessCSS(styles) { return styles; } -// Replace the cache system -// Don't update the local store if the file wasn't modified -//if (lastModified && cache) { -// logger.info('saving ' + href + ' to cache.'); -// try { -// cache.setItem(href, styles); -// cache.setItem(href + ':timestamp', lastModified); -// } catch(e) { -// //TODO - could do with adding more robust error handling -// logger.error('failed to save'); -// }// -//} - - function clone(obj) { var cloned = {}; for(var prop in obj) { @@ -162,13 +148,8 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { if (webInfo) { webInfo.remaining = remaining; - var css = cache && cache.getItem(path), - timestamp = cache && cache.getItem(path + ':timestamp'); - - if (!reload && timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { - // Use local copy + var css = cache.getCSS(path, webInfo); + if (!reload && css) { browser.createCSS(window.document, css, sheet); webInfo.local = true; callback(null, null, data, sheet, webInfo, path); @@ -209,7 +190,8 @@ function initRunningMode(){ errors.add(e, e.href || sheet.href); } else if (css) { css = postProcessCSS(css); - browser.createCSS(window.document, css, sheet, context.lastModified); + browser.createCSS(window.document, css, sheet); + cache.setCSS(sheet.href, context.lastModified, css); } }); } @@ -235,12 +217,6 @@ if (/!watch/.test(location.hash)) { less.watch(); } -if (less.env != 'development') { - try { - cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; - } catch (_) {} -} - // // Get all tags with the 'rel' attribute set to "stylesheet/less" // @@ -276,7 +252,8 @@ less.refresh = function (reload, modifyVars) { } else { less.logger.info("rendered " + sheet.href + " successfully."); css = postProcessCSS(css); - browser.createCSS(window.document, css, sheet, webInfo.lastModified); + browser.createCSS(window.document, css, sheet); + cache.setCSS(sheet.href, webInfo.lastModified, css); } less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); if (webInfo.remaining === 0) { From e6cd9ba69e784732193c6eb011139ca24970c94d Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 9 Oct 2014 05:30:33 +0100 Subject: [PATCH 121/161] remove 2 files no longer used --- build/browser-header.js | 4 ---- build/require.js | 7 ------- 2 files changed, 11 deletions(-) delete mode 100644 build/browser-header.js delete mode 100644 build/require.js diff --git a/build/browser-header.js b/build/browser-header.js deleted file mode 100644 index eaff5bfc4..000000000 --- a/build/browser-header.js +++ /dev/null @@ -1,4 +0,0 @@ -if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; } -less = window.less; -tree = window.less.tree = {}; -less.mode = 'browser'; diff --git a/build/require.js b/build/require.js deleted file mode 100644 index 6e9dfa606..000000000 --- a/build/require.js +++ /dev/null @@ -1,7 +0,0 @@ -// -// Stub out `require` in the browser -// -function require(arg) { - return window.less[arg.split('/')[1]]; -} - From 36a1d5e8e4e29c45f31b34b8136bfb103030f783 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 9 Oct 2014 05:42:55 +0100 Subject: [PATCH 122/161] Fix some links and version numbers --- bower.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bower.json b/bower.json index 12d8df94e..6ad5b5de3 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "less", - "version": "1.7.5", - "main": "./dist/less-1.7.5.js", + "version": "2.0.0", + "main": "./dist/less.js", "ignore": [ "**/.*", "benchmark", diff --git a/package.json b/package.json index c3022d248..54bdaf78b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "less", - "version": "1.7.5", + "version": "2.0.0", "description": "Leaner CSS", "homepage": "http://lesscss.org", "author": { @@ -31,7 +31,7 @@ "test": "./test" }, "jam": { - "main": "./dist/less-1.7.5.js" + "main": "./dist/less.js" }, "engines": { "node": ">=0.10.0" From d3ca207fe8cc890ef322555d94045cc0218c428e Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 9 Oct 2014 17:11:47 +0100 Subject: [PATCH 123/161] jsdoc and add file manager --- lib/less/plugin-manager.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/less/plugin-manager.js b/lib/less/plugin-manager.js index 721faf4d5..922885c37 100644 --- a/lib/less/plugin-manager.js +++ b/lib/less/plugin-manager.js @@ -11,6 +11,11 @@ PluginManager.prototype.addPlugin = function(plugin, options) { this.installedPlugins.push(plugin); plugin.install(this.less, this, options); }; +/** + * Adds a visitor. The visitor object has options on itself to determine + * when it should run. + * @param visitor + */ PluginManager.prototype.addVisitor = function(visitor) { this.visitors.push(visitor); }; @@ -28,6 +33,18 @@ PluginManager.prototype.addPostProcessor = function(postProcessor, priority) { } this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority}); }; +/** + * Adds a file manager + * @param manager + */ +PluginManager.prototype.addFileManager = function(manager) { + // TODO +}; +/** + * Gets all post processors + * @returns {Array} + * @private + */ PluginManager.prototype.getPostProcessors = function() { var postProcessors = []; for(var i = 0; i < this.postProcessors.length; i++) { @@ -35,6 +52,11 @@ PluginManager.prototype.getPostProcessors = function() { } return postProcessors; }; +/** + * Get all visitors + * @returns {Array} + * @private + */ PluginManager.prototype.getVisitors = function() { return this.visitors; }; From 9869b29c3d9fd8f902299e8796d62387c7d7cec0 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 9 Oct 2014 17:45:30 +0100 Subject: [PATCH 124/161] Add file manager to plugin system --- bin/lessc | 21 +++++++---- lib/less-node/index.js | 2 +- ...ode-plugin-manager.js => plugin-loader.js} | 34 +++++++++--------- lib/less/contexts.js | 6 ++-- lib/less/environment/environment.js | 8 +++-- lib/less/parse-tree.js | 5 ++- lib/less/plugin-manager.js | 36 +++++++++++++++---- lib/less/render.js | 13 +++++-- lib/less/transform-tree.js | 4 +-- 9 files changed, 86 insertions(+), 43 deletions(-) rename lib/less-node/{node-plugin-manager.js => plugin-loader.js} (57%) diff --git a/bin/lessc b/bin/lessc index 9a497bdc1..c87eac0cb 100755 --- a/bin/lessc +++ b/bin/lessc @@ -6,7 +6,9 @@ var path = require('path'), mkdirp; var less = require('../lib/less-node'), - pluginManager = new less.PluginManager(less); + pluginLoader = new less.PluginLoader(less), + plugin, + plugins = []; var args = process.argv.slice(1); var silent = false, @@ -27,7 +29,8 @@ var silent = false, strictUnits: false, globalVariables: '', modifyVariables: '', - urlArgs: '' + urlArgs: '', + plugins: plugins }; var sourceMapOptions = {}; var continueProcessing = true, @@ -66,7 +69,7 @@ var sourceMapFileInline = false; function printUsage() { less.lesscHelper.printUsage(); - pluginManager.printUsage(); + pluginLoader.printUsage(plugins); continueProcessing = false; } @@ -220,7 +223,10 @@ args = args.filter(function (arg) { name = splitupArg[1], pluginOptions = splitupArg[3]; - if (!pluginManager.tryLoadPlugin(name, pluginOptions)) { + plugin = pluginLoader.tryLoadPlugin(name, pluginOptions); + if (plugin) { + plugins.push(plugin); + } else { console.log("Unable to load plugin " + name + " please make sure that it is installed under at the same level as less"); console.log(); printUsage(); @@ -228,7 +234,10 @@ args = args.filter(function (arg) { } break; default: - if (!pluginManager.tryLoadPlugin("less-plugin-" + arg, match[2])) { + plugin = pluginLoader.tryLoadPlugin("less-plugin-" + arg, match[2]); + if (plugin) { + plugins.push(plugin); + } else { console.log("Unable to interpret argument " + arg + " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under at the same level as less"); console.log(); printUsage(); @@ -362,8 +371,6 @@ var parseLessFile = function (e, data) { options.sourceMap = sourceMapOptions; } - options.plugins = pluginManager; - less.logger.addListener({ info: function(msg) { if (verbose) { diff --git a/lib/less-node/index.js b/lib/less-node/index.js index 83926908c..ce94a18ed 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -8,7 +8,7 @@ var environment = require("./environment"), // allow people to create less with their own environment less.createFromEnvironment = createFromEnvironment; less.lesscHelper = lesscHelper; -less.PluginManager = require("./node-plugin-manager"); +less.PluginLoader = require("./plugin-loader"); less.fs = require("./fs"); less.fileImport = require("./file-import.js"); less.urlImport = require("./url-import.js"); diff --git a/lib/less-node/node-plugin-manager.js b/lib/less-node/plugin-loader.js similarity index 57% rename from lib/less-node/node-plugin-manager.js rename to lib/less-node/plugin-loader.js index a013fcca0..428b1efff 100644 --- a/lib/less-node/node-plugin-manager.js +++ b/lib/less-node/plugin-loader.js @@ -1,28 +1,28 @@ -var PluginManager = require("../less/plugin-manager"); /** - * Node Plugin Manager + * Node Plugin Loader */ -var NodePluginManager = function(less) { - PluginManager.call(this, less); +var PluginLoader = function(less) { + this.less = less; }; -NodePluginManager.prototype = new PluginManager(); -NodePluginManager.prototype.tryLoadPlugin = function(name, argument) { +PluginLoader.prototype.tryLoadPlugin = function(name, argument) { var plugin = this.tryRequirePlugin(name); if (plugin) { if (plugin.minVersion) { if (this.compareVersion(plugin.minVersion, this.less.version) < 0) { console.log("plugin " + name + " requires version " + this.versionToString(plugin.minVersion)); - return false; + return null; } } else { console.log("plugin has no min version"); } - this.addPlugin(plugin, argument); - return true; + if (argument) { + plugin.setOptions(argument); + } + return plugin; } - return false; + return null; }; -NodePluginManager.prototype.compareVersion = function(aVersion, bVersion) { +PluginLoader.prototype.compareVersion = function(aVersion, bVersion) { for(var i = 0; i < aVersion.length; i++) { if (aVersion[i] !== bVersion[i]) { return aVersion[i] > bVersion[i] ? -1 : 1; @@ -30,14 +30,14 @@ NodePluginManager.prototype.compareVersion = function(aVersion, bVersion) { } return 0; }; -NodePluginManager.prototype.versionToString = function(version) { +PluginLoader.prototype.versionToString = function(version) { var versionString = ""; for(var i = 0; i < version.length; i++) { versionString += (versionString ? "." : "") + version[i]; } return versionString; }; -NodePluginManager.prototype.tryRequirePlugin = function(name) { +PluginLoader.prototype.tryRequirePlugin = function(name) { if (name[0] !== '.') { try { return require(name); @@ -51,12 +51,12 @@ NodePluginManager.prototype.tryRequirePlugin = function(name) { catch(e) { } }; -NodePluginManager.prototype.printUsage = function() { - for(var i = 0; i < this.installedPlugins.length; i++) { - var plugin = this.installedPlugins[i]; +PluginLoader.prototype.printUsage = function(plugins) { + for(var i = 0; i < plugins.length; i++) { + var plugin = plugins[i]; if (plugin.printUsage) { plugin.printUsage(); } } }; -module.exports = NodePluginManager; +module.exports = PluginLoader; diff --git a/lib/less/contexts.js b/lib/less/contexts.js index 96335a7a9..e3de368e6 100644 --- a/lib/less/contexts.js +++ b/lib/less/contexts.js @@ -30,7 +30,8 @@ var parseCopyProperties = [ // context 'processImports', // option & context - whether to process imports. if false then imports will not be imported. // Used by the import manager to stop multiple import visitors being created. - 'reference' // Used to indicate that the contents are imported by reference + 'reference', // Used to indicate that the contents are imported by reference + 'pluginManager' // Used as the plugin manager for the session ]; contexts.Parse = function(options) { @@ -47,7 +48,8 @@ var evalCopyProperties = [ 'sourceMap', // whether to output a source map 'importMultiple', // whether we are currently importing multiple copies 'urlArgs', // whether to add args into url tokens - 'javascriptEnabled'// option - whether JavaScript is enabled. if undefined, defaults to true + 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true + 'pluginManager' // Used as the plugin manager for the session ]; contexts.Eval = function(options, frames) { diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js index 84b4160f4..c0e3f50e9 100644 --- a/lib/less/environment/environment.js +++ b/lib/less/environment/environment.js @@ -19,8 +19,12 @@ environment.prototype.warn = function (msg) { }; environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { - for(var i = this.fileManagers.length - 1; i >= 0 ; i--) { - var fileManager = this.fileManagers[i]; + var fileManagers = this.fileManagers; + if (options.pluginManager) { + fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers()); + } + for(var i = fileManagers.length - 1; i >= 0 ; i--) { + var fileManager = fileManagers[i]; if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) { return fileManager; } diff --git a/lib/less/parse-tree.js b/lib/less/parse-tree.js index f04749f97..6b64709d3 100644 --- a/lib/less/parse-tree.js +++ b/lib/less/parse-tree.js @@ -32,10 +32,9 @@ ParseTree.prototype.toCSS = function(options) { throw new LessError(e, this.imports); } - if (options.plugins) { - var postProcessors = options.plugins.getPostProcessors(); + if (options.pluginManager) { + var postProcessors = options.pluginManager.getPostProcessors(); for(var i = 0; i < postProcessors.length; i++) { - // TODO - async result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports }); } } diff --git a/lib/less/plugin-manager.js b/lib/less/plugin-manager.js index 922885c37..f2051a0e1 100644 --- a/lib/less/plugin-manager.js +++ b/lib/less/plugin-manager.js @@ -6,10 +6,26 @@ var PluginManager = function(less) { this.visitors = []; this.postProcessors = []; this.installedPlugins = []; + this.fileManagers = []; }; -PluginManager.prototype.addPlugin = function(plugin, options) { +/** + * Adds all the plugins in the array + * @param {Array} plugins + */ +PluginManager.prototype.addPlugins = function(plugins) { + if (plugins) { + for(var i = 0;i < plugins.length; i++) { + this.addPlugin(plugins[i]); + } + } +}; +/** + * + * @param plugin + */ +PluginManager.prototype.addPlugin = function(plugin) { this.installedPlugins.push(plugin); - plugin.install(this.less, this, options); + plugin.install(this.less, this); }; /** * Adds a visitor. The visitor object has options on itself to determine @@ -34,14 +50,14 @@ PluginManager.prototype.addPostProcessor = function(postProcessor, priority) { this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority}); }; /** - * Adds a file manager + * * @param manager */ PluginManager.prototype.addFileManager = function(manager) { - // TODO + this.fileManagers.push(manager); }; /** - * Gets all post processors + * * @returns {Array} * @private */ @@ -53,11 +69,19 @@ PluginManager.prototype.getPostProcessors = function() { return postProcessors; }; /** - * Get all visitors + * * @returns {Array} * @private */ PluginManager.prototype.getVisitors = function() { return this.visitors; }; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getFileManagers = function() { + return this.fileManagers; +}; module.exports = PluginManager; diff --git a/lib/less/render.js b/lib/less/render.js index 6159df21e..5013b77dd 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -1,6 +1,7 @@ var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, contexts = require("./contexts"), - Parser = require('./parser/parser'); + Parser = require('./parser/parser'), + PluginManager = require('./plugin-manager'); module.exports = function(environment, ParseTree, ImportManager) { var render = function (input, options, callback) { @@ -20,8 +21,14 @@ module.exports = function(environment, ParseTree, ImportManager) { callback(error); }); } else { - var context = new contexts.Parse(options), - rootFileInfo; + var context, + rootFileInfo, + pluginManager = new PluginManager(); + + pluginManager.addPlugins(options.plugins); + options.pluginManager = pluginManager; + + context = new contexts.Parse(options); if (options.rootFileInfo) { rootFileInfo = options.rootFileInfo; diff --git a/lib/less/transform-tree.js b/lib/less/transform-tree.js index 2a0c69f8e..5a456de36 100644 --- a/lib/less/transform-tree.js +++ b/lib/less/transform-tree.js @@ -43,8 +43,8 @@ module.exports = function(root, options) { new(visitor.ToCSSVisitor)({compress: Boolean(options.compress)}) ], i; - if (options.plugins) { - var pluginVisitors = options.plugins.getVisitors(); + if (options.pluginManager) { + var pluginVisitors = options.pluginManager.getVisitors(); for(i =0; i < pluginVisitors.length; i++) { var pluginVisitor = pluginVisitors[i]; if (pluginVisitor.isPreEvalVisitor) { From a3442404237096881e245e40cb6cde167b5c092f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 9 Oct 2014 17:48:56 +0100 Subject: [PATCH 125/161] Remove unused warn function (replaced with logger) and allow null environments --- lib/less-browser/environment.js | 5 ----- lib/less-browser/index.js | 2 +- lib/less/environment/environment.js | 7 +++---- 3 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 lib/less-browser/environment.js diff --git a/lib/less-browser/environment.js b/lib/less-browser/environment.js deleted file mode 100644 index 6adc7d66e..000000000 --- a/lib/less-browser/environment.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - warn: function warn(msg) { - console.warn(msg); - } -}; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 801c5bd46..656ee9e2e 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -14,7 +14,7 @@ var less; var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol), options = window.less || {}; -window.less = less = require('../less')(require("./environment")); +window.less = less = require('../less')(); var environment = less.environment, browserImport = require("./browser-import")(options, isFileProtocol, less.logger); environment.addFileManager(browserImport); diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js index c0e3f50e9..be654da99 100644 --- a/lib/less/environment/environment.js +++ b/lib/less/environment/environment.js @@ -1,7 +1,9 @@ var environment = function(externalEnvironment, fileManagers) { this.fileManagers = fileManagers || []; + externalEnvironment = externalEnvironment || {}; + var optionalFunctions = ["encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"], - requiredFunctions = ["warn"], + requiredFunctions = [], functions = requiredFunctions.concat(optionalFunctions); for(var i = 0; i < functions.length; i++) { @@ -15,9 +17,6 @@ var environment = function(externalEnvironment, fileManagers) { } }; -environment.prototype.warn = function (msg) { -}; - environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { var fileManagers = this.fileManagers; if (options.pluginManager) { From c0f60cccf8f2309eb76c474781da683bf4278afb Mon Sep 17 00:00:00 2001 From: Luke Page Date: Thu, 9 Oct 2014 18:21:31 +0100 Subject: [PATCH 126/161] Fix plugin issues --- bin/lessc | 4 ++-- lib/less-node/plugin-loader.js | 15 ++++++++++++--- lib/less/render.js | 4 ++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/bin/lessc b/bin/lessc index c87eac0cb..fe65b4fd8 100755 --- a/bin/lessc +++ b/bin/lessc @@ -227,7 +227,7 @@ args = args.filter(function (arg) { if (plugin) { plugins.push(plugin); } else { - console.log("Unable to load plugin " + name + " please make sure that it is installed under at the same level as less"); + console.log("Unable to load plugin " + name + " please make sure that it is installed under or at the same level as less"); console.log(); printUsage(); currentErrorcode = 1; @@ -238,7 +238,7 @@ args = args.filter(function (arg) { if (plugin) { plugins.push(plugin); } else { - console.log("Unable to interpret argument " + arg + " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under at the same level as less"); + console.log("Unable to interpret argument " + arg + " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under or at the same level as less"); console.log(); printUsage(); currentErrorcode = 1; diff --git a/lib/less-node/plugin-loader.js b/lib/less-node/plugin-loader.js index 428b1efff..06c70a7be 100644 --- a/lib/less-node/plugin-loader.js +++ b/lib/less-node/plugin-loader.js @@ -12,11 +12,20 @@ PluginLoader.prototype.tryLoadPlugin = function(name, argument) { console.log("plugin " + name + " requires version " + this.versionToString(plugin.minVersion)); return null; } - } else { - console.log("plugin has no min version"); } if (argument) { - plugin.setOptions(argument); + if (!plugin.setOptions) { + console.log("options have been provided but the plugin " + name + "does not support any options"); + return null; + } + try { + plugin.setOptions(argument); + } + catch(e) { + console.log("Error setting options on plugin " + name); + console.log(e.message); + return null; + } } return plugin; } diff --git a/lib/less/render.js b/lib/less/render.js index 5013b77dd..2a164f906 100644 --- a/lib/less/render.js +++ b/lib/less/render.js @@ -13,7 +13,7 @@ module.exports = function(environment, ParseTree, ImportManager) { } if (callback) { - render(input, options) + render.call(this, input, options) .then(function(css) { callback(null, css); }, @@ -23,7 +23,7 @@ module.exports = function(environment, ParseTree, ImportManager) { } else { var context, rootFileInfo, - pluginManager = new PluginManager(); + pluginManager = new PluginManager(this); pluginManager.addPlugins(options.plugins); options.pluginManager = pluginManager; From b966cf1d1b273ee1c553a9c193603e50ad4887da Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 11 Oct 2014 17:51:08 +0100 Subject: [PATCH 127/161] some renames, tidy ups and bugfixes whilst adding npm plugin --- .../{browser-import.js => file-manager.js} | 16 ++++++++-------- lib/less-browser/index.js | 10 ++++++---- .../{file-import.js => file-manager.js} | 14 +++++++------- lib/less-node/index.js | 10 +++++----- .../{url-import.js => url-file-manager.js} | 10 +++++----- lib/less/environment/abstract-file-manager.js | 4 ++-- 6 files changed, 33 insertions(+), 31 deletions(-) rename lib/less-browser/{browser-import.js => file-manager.js} (86%) rename lib/less-node/{file-import.js => file-manager.js} (82%) rename lib/less-node/{url-import.js => url-file-manager.js} (84%) diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/file-manager.js similarity index 86% rename from lib/less-browser/browser-import.js rename to lib/less-browser/file-manager.js index e5279c026..cf1cc5033 100644 --- a/lib/less-browser/browser-import.js +++ b/lib/less-browser/file-manager.js @@ -24,21 +24,21 @@ function getXMLHttpRequest() { } } -var BrowserImport = function() { +var FileManager = function() { }; -BrowserImport.prototype = new AbstractFileManager(); +FileManager.prototype = new AbstractFileManager(); -BrowserImport.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() { +FileManager.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() { return true; }; -BrowserImport.prototype.join = function join(basePath, laterPath) { +FileManager.prototype.join = function join(basePath, laterPath) { if (!basePath) { return laterPath; } return this.extractUrlParts(laterPath, basePath).path; }; -BrowserImport.prototype.doXHR = function doXHR(url, type, callback, errback) { +FileManager.prototype.doXHR = function doXHR(url, type, callback, errback) { var xhr = getXMLHttpRequest(); var async = isFileProtocol ? options.fileAsync : options.async; @@ -76,11 +76,11 @@ BrowserImport.prototype.doXHR = function doXHR(url, type, callback, errback) { handleResponse(xhr, callback, errback); } }; -BrowserImport.prototype.supports = function(filename, currentDirectory, options, environment) { +FileManager.prototype.supports = function(filename, currentDirectory, options, environment) { return true; }; -BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment) { +FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment) { return new PromiseConstructor(function(fullfill, reject) { if (currentDirectory && !this.isPathAbsolute(filename)) { filename = currentDirectory + filename; @@ -115,5 +115,5 @@ BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, }.bind(this)); }; -return new BrowserImport(); +return FileManager; }; diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 656ee9e2e..fcad374a3 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -16,8 +16,10 @@ var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window window.less = less = require('../less')(); var environment = less.environment, - browserImport = require("./browser-import")(options, isFileProtocol, less.logger); -environment.addFileManager(browserImport); + FileManager = require("./file-manager")(options, isFileProtocol, less.logger), + fileManager = new FileManager(); +environment.addFileManager(fileManager); +less.FileManager = FileManager; require("./log-listener")(less, options); //var utils = require("./utils"); @@ -129,7 +131,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { instanceOptions.useFileCache = true; } - browserImport.loadFile(sheet.href, null, instanceOptions, environment) + fileManager.loadFile(sheet.href, null, instanceOptions, environment) .then(function loadInitialFileCallback(loadedFile) { var data = loadedFile.contents, @@ -137,7 +139,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { webInfo = loadedFile.webInfo; var newFileInfo = { - currentDirectory: browserImport.getPath(path), + currentDirectory: fileManager.getPath(path), filename: path, rootFilename: path, relativeUrls: instanceOptions.relativeUrls}; diff --git a/lib/less-node/file-import.js b/lib/less-node/file-manager.js similarity index 82% rename from lib/less-node/file-import.js rename to lib/less-node/file-manager.js index 84734b71c..a3c27fefd 100644 --- a/lib/less-node/file-import.js +++ b/lib/less-node/file-manager.js @@ -3,19 +3,19 @@ var path = require('path'), PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, AbstractFileManager = require("../less/environment/abstract-file-manager.js"); -var FileImport = function() { +var FileManager = function() { }; -FileImport.prototype = new AbstractFileManager(); +FileManager.prototype = new AbstractFileManager(); -FileImport.prototype.supports = function(filename, currentDirectory, options, environment) { +FileManager.prototype.supports = function(filename, currentDirectory, options, environment) { return true; }; -FileImport.prototype.supportsSync = function(filename, currentDirectory, options, environment) { +FileManager.prototype.supportsSync = function(filename, currentDirectory, options, environment) { return true; }; -FileImport.prototype.loadFile = function(filename, currentDirectory, options, environment) { +FileManager.prototype.loadFile = function(filename, currentDirectory, options, environment) { return new PromiseConstructor(function(fullfill, reject) { var fullFilename, data; @@ -71,9 +71,9 @@ FileImport.prototype.loadFile = function(filename, currentDirectory, options, en }); }; -FileImport.prototype.loadFileSync = function(filename, currentDirectory, options, environment) { +FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment) { filename = path.join(currentDirectory, filename); return { contents: fs.readFileSync(filename), filename: filename }; }; -module.exports = new FileImport(); +module.exports = FileManager; diff --git a/lib/less-node/index.js b/lib/less-node/index.js index ce94a18ed..d29a35ecc 100644 --- a/lib/less-node/index.js +++ b/lib/less-node/index.js @@ -1,8 +1,8 @@ var environment = require("./environment"), - fileImport = require("./file-import.js"), - urlImport = require("./url-import.js"), + FileManager = require("./file-manager"), + UrlFileManager = require("./url-file-manager"), createFromEnvironment = require("../less"), - less = createFromEnvironment(environment, [fileImport, urlImport]), + less = createFromEnvironment(environment, [new FileManager(), new UrlFileManager()]), lesscHelper = require('./lessc-helper'); // allow people to create less with their own environment @@ -10,8 +10,8 @@ less.createFromEnvironment = createFromEnvironment; less.lesscHelper = lesscHelper; less.PluginLoader = require("./plugin-loader"); less.fs = require("./fs"); -less.fileImport = require("./file-import.js"); -less.urlImport = require("./url-import.js"); +less.FileManager = FileManager; +less.UrlFileManager = UrlFileManager; less.formatError = function(ctx, options) { options = options || {}; diff --git a/lib/less-node/url-import.js b/lib/less-node/url-file-manager.js similarity index 84% rename from lib/less-node/url-import.js rename to lib/less-node/url-file-manager.js index 63a20416b..9548c7e30 100644 --- a/lib/less-node/url-import.js +++ b/lib/less-node/url-file-manager.js @@ -5,16 +5,16 @@ var isUrlRe = /^(?:https?:)?\/\//i, AbstractFileManager = require("../less/environment/abstract-file-manager.js"), logger = require("../less/logger"); -var UrlImport = function() { +var UrlFileManager = function() { }; -UrlImport.prototype = new AbstractFileManager(); +UrlFileManager.prototype = new AbstractFileManager(); -UrlImport.prototype.supports = function(filename, currentDirectory, options, environment) { +UrlFileManager.prototype.supports = function(filename, currentDirectory, options, environment) { return isUrlRe.test( filename ) || isUrlRe.test(currentDirectory); }; -UrlImport.prototype.loadFile = function(filename, currentDirectory, options, environment) { +UrlFileManager.prototype.loadFile = function(filename, currentDirectory, options, environment) { return new PromiseConstructor(function(fullfill, reject) { if (request === undefined) { try { request = require('request'); } @@ -50,4 +50,4 @@ UrlImport.prototype.loadFile = function(filename, currentDirectory, options, env }); }; -module.exports = new UrlImport(); +module.exports = UrlFileManager; diff --git a/lib/less/environment/abstract-file-manager.js b/lib/less/environment/abstract-file-manager.js index 1ca31b201..e5e218d48 100644 --- a/lib/less/environment/abstract-file-manager.js +++ b/lib/less/environment/abstract-file-manager.js @@ -70,7 +70,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba } // Stylesheets in IE don't always return the full path - if (!urlParts[1] || urlParts[2]) { + if (baseUrl && (!urlParts[1] || urlParts[2])) { baseUrlParts = baseUrl.match(urlPartsRegex); if (!baseUrlParts) { throw new Error("Could not parse page url - '"+baseUrl+"'"); @@ -102,7 +102,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba returner.hostPart = urlParts[1]; returner.directories = directories; - returner.path = urlParts[1] + directories.join("/"); + returner.path = (urlParts[1] || "") + directories.join("/"); returner.fileUrl = returner.path + (urlParts[4] || ""); returner.url = returner.fileUrl + (urlParts[5] || ""); return returner; From 6d642139966381a30c40a6adface2b93f35a5e97 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 11 Oct 2014 18:51:09 +0100 Subject: [PATCH 128/161] Fix issue #2221 --- lib/less/tree/combinator.js | 2 ++ lib/less/tree/ruleset.js | 11 ++++++++++- test/css/css-3.css | 6 ++++++ test/less/css-3.less | 6 ++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js index 5342dc04d..323522dd9 100644 --- a/lib/less/tree/combinator.js +++ b/lib/less/tree/combinator.js @@ -3,8 +3,10 @@ var Node = require("./node"); var Combinator = function (value) { if (value === ' ') { this.value = ' '; + this.emptyOrWhitespace = true; } else { this.value = value ? value.trim() : ""; + this.emptyOrWhitespace = this.value === ""; } }; Combinator.prototype = new Node(); diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 74e8fd351..23c3ec494 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -522,8 +522,17 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) { if (parentSel.length > 0) { newJoinedSelectorEmpty = false; + // /deep/ is a combinator that is valid without anything in front of it + // so if the & does not have a combinator that is "" or " " then + // and there is a combinator on the parent, then grab that. + // this also allows + a { & .b { .a & { ... though not sure why you would want to do that + var combinator = el.combinator, + parentEl = parentSel[0].elements[0]; + if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) { + combinator = parentEl.combinator; + } // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo)); + newJoinedSelector.elements.push(new(Element)(combinator, parentEl.value, el.index, el.currentFileInfo)); newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); } diff --git a/test/css/css-3.css b/test/css/css-3.css index 7f13992b1..2ed044f57 100644 --- a/test/css/css-3.css +++ b/test/css/css-3.css @@ -132,6 +132,12 @@ body ^^ .shadow { .sel /deep/ .b, ::content .sel { type: shadow-dom; +} + /deep/ b { + c: 'd'; +} + /deep/ b[e] { + f: 'g'; } #issue2066 { background: url('/images/icon-team.svg') 0 0 / contain; diff --git a/test/less/css-3.less b/test/less/css-3.less index 312653c07..b35d0f451 100644 --- a/test/less/css-3.less +++ b/test/less/css-3.less @@ -134,6 +134,12 @@ body ^^ .shadow { ::content .sel { type: shadow-dom; } +/deep/ b { + c: 'd'; + &[e]{ + f: 'g'; + } +} #issue2066 { background: url('/images/icon-team.svg') 0 0 / contain; From a1808b89433758793c8d5ec15878f55197f4f758 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 12 Oct 2014 09:28:15 +0100 Subject: [PATCH 129/161] upgrade promise dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 54bdaf78b..3cccf48ed 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "request": "~2.40.0", "mkdirp": "~0.5.0", "source-map": "0.1.x", - "promise": "~5.0.0" + "promise": "~6.0.0" }, "devDependencies": { "diff": "~1.0", From c40069e83ef41290d8856d3ed524bb97394d3d30 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 12 Oct 2014 16:49:38 +0100 Subject: [PATCH 130/161] Fix the API files --- lib/less/environment/api.js | 62 ---------------- lib/less/environment/environment-api.js | 25 +++++++ lib/less/environment/file-manager-api.js | 95 ++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 62 deletions(-) delete mode 100644 lib/less/environment/api.js create mode 100644 lib/less/environment/environment-api.js create mode 100644 lib/less/environment/file-manager-api.js diff --git a/lib/less/environment/api.js b/lib/less/environment/api.js deleted file mode 100644 index 2ba3f6d1f..000000000 --- a/lib/less/environment/api.js +++ /dev/null @@ -1,62 +0,0 @@ -module.exports = { - /** - * Warns the user about something - * @param {String} msg - the message about the warning - */ - warn: function(msg) { - }, - /** - * Converts a string to a base 64 string - * @param str - */ - encodeBase64: function(str) { - }, - /** - * Lookup the mime-type of a filename - * @param filename - */ - mimeLookup: function (filename) { - }, - /** - * Look up the charset of a mime type - * @param mime - */ - charsetLookup: function (mime) { - }, - /** - * - */ - getSourceMapGenerator: function getSourceMapGenerator() { - }, - /** - * gets the path from the filename, e.g. "http://wwe.files.com/ha/ha.less" would return - * "http://wwe.files.com/ha/" - * If the filename is a file e.g. "file.less" it should return the empty string "" - * @param {String} filename - the filename to extract the path. - * @returns {String} - */ - getPath: function (filename) { - }, - /** - * Returns whether the path is absolute, e.g. "/file.less" = true, "file.less" = false - * @param {String} filename - the filename - * @returns {Boolean} - */ - isPathAbsolute: function(filename) { - }, - /** - * Loads a file for an import aynscronously (or syncronously) - * @param {String} filename - the filename - * @param {String} currentDirectory - the current directory we are in - * @param {Object} options - the context/options object - * @param {Function} callback - a function to callback when finished, - * taking the format callback(error, contents, fullfilename, reserved) - * where error is { type: {string}, message: {string} }, contents is {string} and fullfilename is {string} - * for reserved, see less-browser/index.js which uses this argument for cache information - * @returns {Boolean} - */ - loadFile: function(filename, currentDirectory, options, callback) { - }, - supportsDataURI: function() { - } -}; diff --git a/lib/less/environment/environment-api.js b/lib/less/environment/environment-api.js new file mode 100644 index 000000000..ae6beff40 --- /dev/null +++ b/lib/less/environment/environment-api.js @@ -0,0 +1,25 @@ +module.exports = { + /** + * Converts a string to a base 64 string + * @param str + */ + encodeBase64: function(str) { + }, + /** + * Lookup the mime-type of a filename + * @param filename + */ + mimeLookup: function (filename) { + }, + /** + * Look up the charset of a mime type + * @param mime + */ + charsetLookup: function (mime) { + }, + /** + * Gets a source map generator + */ + getSourceMapGenerator: function getSourceMapGenerator() { + } +}; diff --git a/lib/less/environment/file-manager-api.js b/lib/less/environment/file-manager-api.js new file mode 100644 index 000000000..4138b7cb8 --- /dev/null +++ b/lib/less/environment/file-manager-api.js @@ -0,0 +1,95 @@ +module.exports = { + /** + * Given the full path to a file, return the path component + * Provided by AbstractFileManager + * @param {string} filename + * @returns {string} + */ + getPath: function(filename) { + }, + /** + * Whether the rootpath should be converted to be absolute. + * The browser ovverides this to return true because urls must be absolute. + * Provided by AbstractFileManager (returns false) + * @returns {bool} + */ + alwaysMakePathsAbsolute: function() { + }, + /** + * Returns whether a path is absolute + * Provided by AbstractFileManager + * @param {string} path + * @returns {bool} + */ + isPathAbsolute: function(path) { + }, + /** + * joins together 2 paths + * Provided by AbstractFileManager + * @param {string} basePath + * @param {string} laterPath + */ + join: function(basePath, laterPath) { + }, + /** + * Returns the difference between 2 paths + * E.g. url = a/ baseUrl = a/b/ returns ../ + * url = a/b/ baseUrl = a/ returns b/ + * Provided by AbstractFileManager + * @param {string} url + * @param {string} baseUrl + * @returns {string} + */ + pathDiff: function(url, baseUrl) { + }, + /** + * Returns whether this file manager supports this file for syncronous file retrieval + * If true is returned, loadFileSync will then be called with the file. + * Provided by AbstractFileManager (returns false) + * @param {string} filename + * @param {string} currentDirectory + * @param {object} options + * @param {less.environment.environment} environment + * @returns {bool} + */ + supportsSync: function(filename, currentDirectory, options, environment) { + }, + /** + * + * @param {string} filename + * @param {string} currentDirectory + * @param {object} options + * @param {less.environment.environment} environment + * @returns {bool} + */ + supports: function(filename, currentDirectory, options, environment) { + }, + /** + * Loads a file asynchronously. Expects a promise that either rejects with an error or fullfills with an + * object containing + * { filename: - full resolved path to file + * contents: - the contents of the file, as a string } + * + * @param {string} filename + * @param {string} currentDirectory + * @param {object} options + * @param {less.environment.environment} environment + * @returns {Promise} + */ + loadFile: function(filename, currentDirectory, options, environment) { + }, + /** + * Loads a file synchronously. Expects an immediate return with an object containing + * { error: - error object if an error occurs + * filename: - full resolved path to file + * contents: - the contents of the file, as a string } + * + * @param {string} filename + * @param {string} currentDirectory + * @param {object} options + * @param {less.environment.environment} environment + * @returns {object} should be an object containing error or contents and filename + */ + loadFileSync: function(filename, currentDirectory, options, environment) { + } +}; From a0b1b2f4bd35df1fdf467248684a32f3e6dc82c4 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sun, 12 Oct 2014 15:57:57 -0700 Subject: [PATCH 131/161] refresh returns promise pass the error to reject() --- lib/less-browser/index.js | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index fcad374a3..32cca36c0 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -26,6 +26,7 @@ require("./log-listener")(less, options); var errors = require("./error-reporting")(window, less, options); var browser = require("./browser"); var cache = options.cache || require("./cache")(window, options, less.logger); +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; options.env = options.env || (window.location.hostname == '127.0.0.1' || window.location.hostname == '0.0.0.0' || @@ -242,29 +243,33 @@ less.modifyVars = function(record) { }; less.refresh = function (reload, modifyVars) { - var startTime, endTime; - startTime = endTime = new Date(); - - loadStyleSheets(function (e, css, _, sheet, webInfo) { - if (e) { - return errors.add(e, e.href || sheet.href); - } - if (webInfo.local) { - less.logger.info("loading " + sheet.href + " from cache."); - } else { - less.logger.info("rendered " + sheet.href + " successfully."); - css = postProcessCSS(css); - browser.createCSS(window.document, css, sheet); - cache.setCSS(sheet.href, webInfo.lastModified, css); - } - less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); - if (webInfo.remaining === 0) { - less.logger.info("less has finished. css generated in " + (new Date() - startTime) + 'ms'); - } - endTime = new Date(); - }, reload, modifyVars); - - loadStyles(modifyVars); + return new PromiseConstructor(function(resolve, reject) { + var startTime, endTime; + startTime = endTime = new Date(); + + loadStyleSheets(function (e, css, _, sheet, webInfo) { + if (e) { + errors.add(e, e.href || sheet.href); + reject(e); + } + if (webInfo.local) { + less.logger.info("loading " + sheet.href + " from cache."); + } else { + less.logger.info("rendered " + sheet.href + " successfully."); + css = postProcessCSS(css); + browser.createCSS(window.document, css, sheet); + cache.setCSS(sheet.href, webInfo.lastModified, css); + } + less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); + if (webInfo.remaining === 0) { + less.logger.info("less has finished. css generated in " + (new Date() - startTime) + 'ms'); + resolve(); + } + endTime = new Date(); + }, reload, modifyVars); + + loadStyles(modifyVars); + }); }; less.refreshStyles = loadStyles; From 999af91bb38ee1480ea4a3a24e12ee25c34e8a40 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Tue, 14 Oct 2014 00:19:07 -0700 Subject: [PATCH 132/161] set less.hasFinished to refresh() promise fix white space return promise on modifyVars --- lib/less-browser/index.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 32cca36c0..1157d3634 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -239,12 +239,12 @@ for (var i = 0; i < links.length; i++) { // CSS without reloading less-files // less.modifyVars = function(record) { - less.refresh(false, record); + return less.refresh(false, record); }; less.refresh = function (reload, modifyVars) { - return new PromiseConstructor(function(resolve, reject) { - var startTime, endTime; + less.hasFinished = new PromiseConstructor(function (resolve, reject) { + var startTime, endTime, totalMilliseconds; startTime = endTime = new Date(); loadStyleSheets(function (e, css, _, sheet, webInfo) { @@ -262,14 +262,22 @@ less.refresh = function (reload, modifyVars) { } less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); if (webInfo.remaining === 0) { - less.logger.info("less has finished. css generated in " + (new Date() - startTime) + 'ms'); - resolve(); + totalMilliseconds = new Date() - startTime; + less.logger.info("less has finished. css generated in " + totalMilliseconds + 'ms'); + resolve({ + startTime: startTime, + endTime: endTime, + totalMilliseconds: totalMilliseconds, + sheets: less.sheets.length + }); } endTime = new Date(); }, reload, modifyVars); loadStyles(modifyVars); }); + + return less.hasFinished; }; less.refreshStyles = loadStyles; From d31be578a3b35c7b0f43738e26f42576bab48ce9 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Wed, 15 Oct 2014 22:46:20 -0700 Subject: [PATCH 133/161] Fixes #713, #771, #1161 --- lib/less-browser/index.js | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index fcad374a3..5c69aad7d 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -26,6 +26,7 @@ require("./log-listener")(less, options); var errors = require("./error-reporting")(window, less, options); var browser = require("./browser"); var cache = options.cache || require("./cache")(window, options, less.logger); +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; options.env = options.env || (window.location.hostname == '127.0.0.1' || window.location.hostname == '0.0.0.0' || @@ -222,16 +223,23 @@ if (/!watch/.test(location.hash)) { // // Get all tags with the 'rel' attribute set to "stylesheet/less" // -var links = document.getElementsByTagName('link'); - -less.sheets = []; +less.registerStylesheets = function(callback) { + return new PromiseConstructor(function(resolve, reject) { + var links = document.getElementsByTagName('link'); + less.sheets = []; + + for (var i = 0; i < links.length; i++) { + if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && + (links[i].type.match(typePattern)))) { + less.sheets.push(links[i]); + } -for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - less.sheets.push(links[i]); - } -} + if (i === links.length - 1) { + resolve(); + } + } + }); +}; // // With this function, it's possible to alter variables and re-render @@ -269,4 +277,8 @@ less.refresh = function (reload, modifyVars) { less.refreshStyles = loadStyles; -less.refresh(less.env === 'development'); +less.registerStylesheets().then( + function () { + less.refresh(less.env === 'development'); + } +); From 89358faefe8a577cb4adc8cf4c487a5c184a57fe Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Wed, 15 Oct 2014 23:02:11 -0700 Subject: [PATCH 134/161] more sane resolve --- lib/less-browser/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 5c69aad7d..97a1a9a0c 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -233,11 +233,9 @@ less.registerStylesheets = function(callback) { (links[i].type.match(typePattern)))) { less.sheets.push(links[i]); } - - if (i === links.length - 1) { - resolve(); - } } + + resolve(); }); }; From 0e821f1a4f563f24e4269799d45d223ea31473b9 Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Wed, 15 Oct 2014 23:03:55 -0700 Subject: [PATCH 135/161] cleanup callback --- lib/less-browser/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 97a1a9a0c..3855e52ac 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -223,7 +223,7 @@ if (/!watch/.test(location.hash)) { // // Get all tags with the 'rel' attribute set to "stylesheet/less" // -less.registerStylesheets = function(callback) { +less.registerStylesheets = function() { return new PromiseConstructor(function(resolve, reject) { var links = document.getElementsByTagName('link'); less.sheets = []; From 8b42ae388305a9c8e38ddbbc97f0f374d96342c2 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Fri, 17 Oct 2014 08:12:52 +0100 Subject: [PATCH 136/161] A few tweaks to browser loading --- lib/less-browser/index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index e5219c26e..b6420f760 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -248,7 +248,7 @@ less.modifyVars = function(record) { }; less.refresh = function (reload, modifyVars) { - less.hasFinished = new PromiseConstructor(function (resolve, reject) { + return new PromiseConstructor(function (resolve, reject) { var startTime, endTime, totalMilliseconds; startTime = endTime = new Date(); @@ -278,17 +278,15 @@ less.refresh = function (reload, modifyVars) { } endTime = new Date(); }, reload, modifyVars); - + loadStyles(modifyVars); }); - - return less.hasFinished; }; less.refreshStyles = loadStyles; -less.registerStylesheets().then( +less.pageLoadFinished = less.registerStylesheets().then( function () { - less.refresh(less.env === 'development'); + return less.refresh(less.env === 'development'); } ); From 55baaa5db112aea5ff4aeaac299b720d66ee76af Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 07:48:47 +0100 Subject: [PATCH 137/161] use page load finished in tests --- test/browser/common.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/browser/common.js b/test/browser/common.js index 40ec8eb99..31f248f35 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -54,17 +54,21 @@ var testSheet = function(sheet) { lessOutputObj, lessOutput, expectedOutputHref = document.getElementById(expectedOutputId).href, - expectedOutput = loadFile(expectedOutputHref); + expectedOutput = loadFile(expectedOutputHref), + finished = false; // Browser spec generates less on the fly, so we need to loose control + less.pageLoadFinished + .then(function() { + finished = true; + }); waitsFor(function() { - lessOutputObj = document.getElementById(lessOutputId); - // the type condition is necessary because of inline browser tests - return lessOutputObj !== null && lessOutputObj.type === "text/css"; + return finished; }, "generation of " + lessOutputId + "", 700); runs(function() { - lessOutput = lessOutputObj.innerText || lessOutputObj.innerHTML; + lessOutputObj = document.getElementById(lessOutputId); + lessOutput = lessOutputObj.innerText || lessOutputObj.innerHTML; }); waitsFor(function() { From 5796cf29e0f717277750b279ebcda8c90bdb9850 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 08:34:54 +0100 Subject: [PATCH 138/161] upgrade browserify. Use the promise shim instead of the full node version in the browser. --- Gruntfile.js | 3 +++ lib/less-browser/index.js | 8 +++++--- package.json | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b52fef8df..00393c06f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -50,6 +50,9 @@ module.exports = function (grunt) { browserify: { browser: { src: ['./lib/less-browser/index.js'], + options: { + ignore: ["promise"] + }, dest: 'tmp/less.js' } }, diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index b6420f760..f90d2602f 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -14,6 +14,9 @@ var less; var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol), options = window.less || {}; +// shim Promise if required +require('promise/polyfill.js'); + window.less = less = require('../less')(); var environment = less.environment, FileManager = require("./file-manager")(options, isFileProtocol, less.logger), @@ -26,7 +29,6 @@ require("./log-listener")(less, options); var errors = require("./error-reporting")(window, less, options); var browser = require("./browser"); var cache = options.cache || require("./cache")(window, options, less.logger); -var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise; options.env = options.env || (window.location.hostname == '127.0.0.1' || window.location.hostname == '0.0.0.0' || @@ -224,7 +226,7 @@ if (/!watch/.test(location.hash)) { // Get all tags with the 'rel' attribute set to "stylesheet/less" // less.registerStylesheets = function() { - return new PromiseConstructor(function(resolve, reject) { + return new Promise(function(resolve, reject) { var links = document.getElementsByTagName('link'); less.sheets = []; @@ -248,7 +250,7 @@ less.modifyVars = function(record) { }; less.refresh = function (reload, modifyVars) { - return new PromiseConstructor(function (resolve, reject) { + return new Promise(function (resolve, reject) { var startTime, endTime, totalMilliseconds; startTime = endTime = new Date(); diff --git a/package.json b/package.json index 3cccf48ed..1b6658cee 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "matchdep": "~0.3.0", "time-grunt": "~0.3.1", "browserify": "~3.30.2", - "grunt-browserify": "~1.3.1" + "grunt-browserify": "~3.0.1" }, "keywords": [ "compile less", From 0bc033008f2c00b73f5e1d1d029c05c4322b6057 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 08:43:21 +0100 Subject: [PATCH 139/161] upgrade uglify --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1b6658cee..87b969a8d 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "grunt-contrib-connect": "~0.7.0", "grunt-contrib-jasmine": "~0.5.2", "grunt-contrib-jshint": "~0.10.0", - "grunt-contrib-uglify": "~0.4.0", + "grunt-contrib-uglify": "~0.6.0", "grunt-shell": "~0.7.0", "http-server": "~0.6.1", "matchdep": "~0.3.0", From 3eccec877cae2e9b3123c3f31c7e82ea9cf8675a Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 08:49:15 +0100 Subject: [PATCH 140/161] module upgrades --- package.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 87b969a8d..fbd623d8c 100644 --- a/package.json +++ b/package.json @@ -40,18 +40,18 @@ "test": "grunt test" }, "optionalDependencies": { - "graceful-fs": "~3.0.2", + "graceful-fs": "~3.0.4", "mime": "~1.2.11", - "request": "~2.40.0", + "request": "~2.45.0", "mkdirp": "~0.5.0", "source-map": "0.1.x", - "promise": "~6.0.0" + "promise": "~6.0.1" }, "devDependencies": { "diff": "~1.0", - "grunt": "~0.4.2", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-concat": "~0.4.0", + "grunt": "~0.4.5", + "grunt-contrib-clean": "~0.6.0", + "grunt-contrib-concat": "~0.5.0", "grunt-contrib-connect": "~0.7.0", "grunt-contrib-jasmine": "~0.5.2", "grunt-contrib-jshint": "~0.10.0", @@ -60,7 +60,6 @@ "http-server": "~0.6.1", "matchdep": "~0.3.0", "time-grunt": "~0.3.1", - "browserify": "~3.30.2", "grunt-browserify": "~3.0.1" }, "keywords": [ From 54a8a38077ec8ebab06ad3dbb8d08817241a3274 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 09:05:19 +0100 Subject: [PATCH 141/161] use connect instead of http server and upgrade some more packages --- Gruntfile.js | 12 +++++------- package.json | 5 ++--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 00393c06f..08a9dd616 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -36,14 +36,11 @@ module.exports = function (grunt) { benchmark: { command: 'node benchmark/less-benchmark.js' }, - "browsertest-server": { - command: 'node node_modules/http-server/bin/http-server . -p 8081' - }, "sourcemap-test": { command: [ 'node bin/lessc --source-map --source-map-map-inline test/less/import.less test/sourcemaps/import.css', - 'node bin/lessc --source-map --source-map-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css', - 'node node_modules/http-server/bin/http-server test/sourcemaps -p 8084'].join('&&') + 'node bin/lessc --source-map --source-map-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css' + ].join('&&') } }, @@ -285,7 +282,7 @@ module.exports = function (grunt) { // setup a web server to run the browser tests in a browser rather than phantom grunt.registerTask('browsertest-server', [ 'browsertest-lessjs', - 'shell:browsertest-server' + 'connect::keepalive' ]); // Run all tests @@ -299,7 +296,8 @@ module.exports = function (grunt) { // generate a good test environment for testing sourcemaps grunt.registerTask('sourcemap-test', [ 'clean:sourcemap-test', - 'shell:sourcemap-test' + 'shell:sourcemap-test', + 'connect::keepalive' ]); // Run benchmark diff --git a/package.json b/package.json index fbd623d8c..510685532 100644 --- a/package.json +++ b/package.json @@ -56,10 +56,9 @@ "grunt-contrib-jasmine": "~0.5.2", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.6.0", - "grunt-shell": "~0.7.0", - "http-server": "~0.6.1", + "grunt-shell": "~1.1.1", "matchdep": "~0.3.0", - "time-grunt": "~0.3.1", + "time-grunt": "~1.0.0", "grunt-browserify": "~3.0.1" }, "keywords": [ From ee94d83f126b25b0ab1e2b4338b4088e287afc7b Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 15:40:21 +0100 Subject: [PATCH 142/161] update packages --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 510685532..a4fca3529 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "grunt": "~0.4.5", "grunt-contrib-clean": "~0.6.0", "grunt-contrib-concat": "~0.5.0", - "grunt-contrib-connect": "~0.7.0", + "grunt-contrib-connect": "~0.8.0", "grunt-contrib-jasmine": "~0.5.2", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.6.0", From 8562e2cad89d7ca2a179fb3e99e659e5f4ec0158 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sat, 18 Oct 2014 23:14:04 +0100 Subject: [PATCH 143/161] Upgrade to jasmine v2 --- package.json | 2 +- test/browser/common.js | 293 ++- test/browser/jasmine-html.js | 681 ------ test/browser/jasmine.css | 82 - test/browser/jasmine.js | 2600 ----------------------- test/browser/runner-main-spec.js | 1 + test/browser/runner-modify-vars-spec.js | 62 +- test/browser/test-runner-template.tmpl | 4 +- 8 files changed, 150 insertions(+), 3575 deletions(-) delete mode 100644 test/browser/jasmine-html.js delete mode 100644 test/browser/jasmine.css delete mode 100644 test/browser/jasmine.js diff --git a/package.json b/package.json index a4fca3529..49b9d2181 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "grunt-contrib-clean": "~0.6.0", "grunt-contrib-concat": "~0.5.0", "grunt-contrib-connect": "~0.8.0", - "grunt-contrib-jasmine": "~0.5.2", + "grunt-contrib-jasmine": "~0.8.0", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-uglify": "~0.6.0", "grunt-shell": "~1.1.1", diff --git a/test/browser/common.js b/test/browser/common.js index 31f248f35..1ca4e7a46 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -1,215 +1,162 @@ /* record log messages for testing */ -// var logAllIds = function() { -// var allTags = document.head.getElementsByTagName('style'); -// var ids = []; -// for (var tg = 0; tg < allTags.length; tg++) { -// var tag = allTags[tg]; -// if (tag.id) { -// console.log(tag.id); -// } -// } -// }; var logMessages = []; window.less = window.less || {}; -less.loggers = [{ - info: function(msg) { - logMessages.push(msg); - }, - debug: function(msg) { - logMessages.push(msg); - }, - warn: function(msg) { - logMessages.push(msg); - }, - error: function(msg) { - logMessages.push(msg); +less.loggers = [ + { + info: function (msg) { + logMessages.push(msg); + }, + debug: function (msg) { + logMessages.push(msg); + }, + warn: function (msg) { + logMessages.push(msg); + }, + error: function (msg) { + logMessages.push(msg); + } } -}]; +]; -var testLessEqualsInDocument = function() { - testLessInDocument(testSheet); +var testLessEqualsInDocument = function () { + testLessInDocument(testSheet); }; -var testLessErrorsInDocument = function(isConsole) { +var testLessErrorsInDocument = function (isConsole) { testLessInDocument(isConsole ? testErrorSheetConsole : testErrorSheet); }; -var testLessInDocument = function(testFunc) { - var links = document.getElementsByTagName('link'), - typePattern = /^text\/(x-)?less$/; +var testLessInDocument = function (testFunc) { + var links = document.getElementsByTagName('link'), + typePattern = /^text\/(x-)?less$/; - for (var i = 0; i < links.length; i++) { - if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && - (links[i].type.match(typePattern)))) { - testFunc(links[i]); + for (var i = 0; i < links.length; i++) { + if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && + (links[i].type.match(typePattern)))) { + testFunc(links[i]); + } } - } }; -var testSheet = function(sheet) { - it(sheet.id + " should match the expected output", function() { - var lessOutputId = sheet.id.replace("original-", ""), - expectedOutputId = "expected-" + lessOutputId, - lessOutputObj, - lessOutput, - expectedOutputHref = document.getElementById(expectedOutputId).href, - expectedOutput = loadFile(expectedOutputHref), - finished = false; - - // Browser spec generates less on the fly, so we need to loose control - less.pageLoadFinished - .then(function() { - finished = true; - }); - waitsFor(function() { - return finished; - }, "generation of " + lessOutputId + "", 700); - - runs(function() { - lessOutputObj = document.getElementById(lessOutputId); - lessOutput = lessOutputObj.innerText || lessOutputObj.innerHTML; - }); - - waitsFor(function() { - return expectedOutput.loaded; - }, "failed to load expected outout", 10000); - - runs(function() { - // use sheet to do testing - expect(expectedOutput.text).toEqual(lessOutput); +var testSheet = function (sheet) { + it(sheet.id + " should match the expected output", function (done) { + var lessOutputId = sheet.id.replace("original-", ""), + expectedOutputId = "expected-" + lessOutputId, + lessOutputObj, + lessOutput, + expectedOutputHref = document.getElementById(expectedOutputId).href, + expectedOutput = loadFile(expectedOutputHref); + + // Browser spec generates less on the fly, so we need to loose control + less.pageLoadFinished + .then(function () { + lessOutputObj = document.getElementById(lessOutputId); + lessOutput = lessOutputObj.innerText || lessOutputObj.innerHTML; + + expectedOutput + .then(function (text) { + expect(text).toEqual(lessOutput); + done(); + }); + }); }); - }); }; //TODO: do it cleaner - the same way as in css function extractId(href) { - return href.replace(/^[a-z-]+:\/+?[^\/]+/i, '') // Remove protocol & domain - .replace(/^\//, '') // Remove root / - .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension - .replace(/[^\.\w-]+/g, '-') // Replace illegal characters - .replace(/\./g, ':'); // Replace dots with colons(for valid id) + return href.replace(/^[a-z-]+:\/+?[^\/]+/i, '') // Remove protocol & domain + .replace(/^\//, '') // Remove root / + .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension + .replace(/[^\.\w-]+/g, '-') // Replace illegal characters + .replace(/\./g, ':'); // Replace dots with colons(for valid id) } -var testErrorSheet = function(sheet) { - it(sheet.id + " should match an error", function() { - var lessHref = sheet.href, - id = "less-error-message:" + extractId(lessHref), - // id = sheet.id.replace(/^original-less:/, "less-error-message:"), - errorHref = lessHref.replace(/.less$/, ".txt"), - errorFile = loadFile(errorHref), - actualErrorElement, - actualErrorMsg; - - // Less.js sets 10ms timer in order to add error message on top of page. - waitsFor(function() { - actualErrorElement = document.getElementById(id); - return actualErrorElement !== null; - }, "error message was not generated", 70); - - runs(function() { - actualErrorMsg = actualErrorElement.innerText - .replace(/\n\d+/g, function(lineNo) { - return lineNo + " "; - }) - .replace(/\n\s*in /g, " in ") - .replace("\n\n", "\n"); +var waitFor = function (waitFunc) { + return new Promise(function (resolve) { + var timeoutId = setInterval(function () { + if (waitFunc()) { + clearInterval(timeoutId); + resolve(); + } + }, 5); }); +}; - waitsFor(function() { - return errorFile.loaded; - }, "failed to load expected outout", 10000); - - runs(function() { - var errorTxt = errorFile.text - .replace("{path}", "") - .replace("{pathrel}", "") - .replace("{pathhref}", "http://localhost:8081/test/less/errors/") - .replace("{404status}", " (404)"); - expect(errorTxt).toEqual(actualErrorMsg); - if (errorTxt == actualErrorMsg) { - actualErrorElement.style.display = "none"; - } - }); +var testErrorSheet = function (sheet) { + it(sheet.id + " should match an error", function (done) { + var lessHref = sheet.href, + id = "less-error-message:" + extractId(lessHref), + errorHref = lessHref.replace(/.less$/, ".txt"), + errorFile = loadFile(errorHref), + actualErrorElement, + actualErrorMsg; + + // Less.js sets 10ms timer in order to add error message on top of page. + waitFor(function () { + actualErrorElement = document.getElementById(id); + return actualErrorElement !== null; + }).then(function () { + actualErrorMsg = actualErrorElement.innerText + .replace(/\n\d+/g, function (lineNo) { + return lineNo + " "; + }) + .replace(/\n\s*in /g, " in ") + .replace("\n\n", "\n"); + errorFile + .then(function (errorTxt) { + errorTxt = errorTxt + .replace("{path}", "") + .replace("{pathrel}", "") + .replace("{pathhref}", "http://localhost:8081/test/less/errors/") + .replace("{404status}", " (404)"); + expect(errorTxt).toEqual(actualErrorMsg); + if (errorTxt == actualErrorMsg) { + actualErrorElement.style.display = "none"; + } + done(); + }); + }); }); }; -var testErrorSheetConsole = function(sheet) { - it(sheet.id + " should match an error", function() { - var lessHref = sheet.href, +var testErrorSheetConsole = function (sheet) { + it(sheet.id + " should match an error", function (done) { + var lessHref = sheet.href, id = sheet.id.replace(/^original-less:/, "less-error-message:"), errorHref = lessHref.replace(/.less$/, ".txt"), errorFile = loadFile(errorHref), actualErrorElement = document.getElementById(id), actualErrorMsg = logMessages[logMessages.length - 1]; - describe("the error", function() { + describe("the error", function () { expect(actualErrorElement).toBe(null); - }); - /*actualErrorMsg = actualErrorElement.innerText - .replace(/\n\d+/g, function(lineNo) { return lineNo + " "; }) - .replace(/\n\s*in /g, " in ") - .replace("\n\n", "\n");*/ - - waitsFor(function() { - return errorFile.loaded; - }, "failed to load expected outout", 10000); - - runs(function() { - var errorTxt = errorFile.text - .replace("{path}", "") - .replace("{pathrel}", "") - .replace("{pathhref}", "http://localhost:8081/browser/less/") - .replace("{404status}", " (404)") - .trim(); - expect(errorTxt).toEqual(actualErrorMsg); + errorFile + .then(function (errorTxt) { + errorTxt + .replace("{path}", "") + .replace("{pathrel}", "") + .replace("{pathhref}", "http://localhost:8081/browser/less/") + .replace("{404status}", " (404)") + .trim(); + expect(errorTxt).toEqual(actualErrorMsg); + done(); + }); }); - }); }; -var loadFile = function(href) { - var request = new XMLHttpRequest(), - response = { - loaded: false, - text: "" - }; - request.open('GET', href, true); - request.onload = function(e) { - response.text = request.response.replace(/\r/g, ""); - response.loaded = true; - }; - request.send(); - return response; +var loadFile = function (href) { + return new Promise(function (resolve, reject) { + var request = new XMLHttpRequest(); + request.open('GET', href, true); + request.onload = function (e) { + resolve(request.response.replace(/\r/g, "")); + }; + request.send(); + }); }; -(function() { - var jasmineEnv = jasmine.getEnv(); - jasmineEnv.updateInterval = 1000; - - var htmlReporter = new jasmine.HtmlReporter(); - - jasmineEnv.addReporter(htmlReporter); - - jasmineEnv.specFilter = function(spec) { - return htmlReporter.specFilter(spec); - }; - - var currentWindowOnload = window.onload; - - window.onload = function() { - if (currentWindowOnload) { - currentWindowOnload(); - } - execJasmine(); - }; - - function execJasmine() { - setTimeout(function() { - jasmineEnv.execute(); - }, 3000); - } - -})(); +jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; diff --git a/test/browser/jasmine-html.js b/test/browser/jasmine-html.js deleted file mode 100644 index 543d56963..000000000 --- a/test/browser/jasmine-html.js +++ /dev/null @@ -1,681 +0,0 @@ -jasmine.HtmlReporterHelpers = {}; - -jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { - var results = child.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - - return status; -}; - -jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { - var parentDiv = this.dom.summary; - var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; - var parent = child[parentSuite]; - - if (parent) { - if (typeof this.views.suites[parent.id] == 'undefined') { - this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); - } - parentDiv = this.views.suites[parent.id].element; - } - - parentDiv.appendChild(childElement); -}; - - -jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { - for(var fn in jasmine.HtmlReporterHelpers) { - ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; - } -}; - -jasmine.HtmlReporter = function(_doc) { - var self = this; - var doc = _doc || window.document; - - var reporterView; - - var dom = {}; - - // Jasmine Reporter Public Interface - self.logRunningSpecs = false; - - self.reportRunnerStarting = function(runner) { - var specs = runner.specs() || []; - - if (specs.length == 0) { - return; - } - - createReporterDom(runner.env.versionString()); - doc.body.appendChild(dom.reporter); - setExceptionHandling(); - - reporterView = new jasmine.HtmlReporter.ReporterView(dom); - reporterView.addSpecs(specs, self.specFilter); - }; - - self.reportRunnerResults = function(runner) { - reporterView && reporterView.complete(); - }; - - self.reportSuiteResults = function(suite) { - reporterView.suiteComplete(suite); - }; - - self.reportSpecStarting = function(spec) { - if (self.logRunningSpecs) { - self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } - }; - - self.reportSpecResults = function(spec) { - reporterView.specComplete(spec); - }; - - self.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } - }; - - self.specFilter = function(spec) { - if (!focusedSpecName()) { - return true; - } - - return spec.getFullName().indexOf(focusedSpecName()) === 0; - }; - - return self; - - function focusedSpecName() { - var specName; - - (function memoizeFocusedSpec() { - if (specName) { - return; - } - - var paramMap = []; - var params = jasmine.HtmlReporter.parameters(doc); - - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - specName = paramMap.spec; - })(); - - return specName; - } - - function createReporterDom(version) { - dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, - dom.banner = self.createDom('div', { className: 'banner' }, - self.createDom('span', { className: 'title' }, "Jasmine "), - self.createDom('span', { className: 'version' }, version)), - - dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), - dom.alert = self.createDom('div', {className: 'alert'}, - self.createDom('span', { className: 'exceptions' }, - self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'), - self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))), - dom.results = self.createDom('div', {className: 'results'}, - dom.summary = self.createDom('div', { className: 'summary' }), - dom.details = self.createDom('div', { id: 'details' })) - ); - } - - function noTryCatch() { - return window.location.search.match(/catch=false/); - } - - function searchWithCatch() { - var params = jasmine.HtmlReporter.parameters(window.document); - var removed = false; - var i = 0; - - while (!removed && i < params.length) { - if (params[i].match(/catch=/)) { - params.splice(i, 1); - removed = true; - } - i++; - } - if (jasmine.CATCH_EXCEPTIONS) { - params.push("catch=false"); - } - - return params.join("&"); - } - - function setExceptionHandling() { - var chxCatch = document.getElementById('no_try_catch'); - - if (noTryCatch()) { - chxCatch.setAttribute('checked', true); - jasmine.CATCH_EXCEPTIONS = false; - } - chxCatch.onclick = function() { - window.location.search = searchWithCatch(); - }; - } -}; -jasmine.HtmlReporter.parameters = function(doc) { - var paramStr = doc.location.search.substring(1); - var params = []; - - if (paramStr.length > 0) { - params = paramStr.split('&'); - } - return params; -} -jasmine.HtmlReporter.sectionLink = function(sectionName) { - var link = '?'; - var params = []; - - if (sectionName) { - params.push('spec=' + encodeURIComponent(sectionName)); - } - if (!jasmine.CATCH_EXCEPTIONS) { - params.push("catch=false"); - } - if (params.length > 0) { - link += params.join("&"); - } - - return link; -}; -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); -jasmine.HtmlReporter.ReporterView = function(dom) { - this.startedAt = new Date(); - this.runningSpecCount = 0; - this.completeSpecCount = 0; - this.passedCount = 0; - this.failedCount = 0; - this.skippedCount = 0; - - this.createResultsMenu = function() { - this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, - this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), - ' | ', - this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); - - this.summaryMenuItem.onclick = function() { - dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); - }; - - this.detailsMenuItem.onclick = function() { - showDetails(); - }; - }; - - this.addSpecs = function(specs, specFilter) { - this.totalSpecCount = specs.length; - - this.views = { - specs: {}, - suites: {} - }; - - for (var i = 0; i < specs.length; i++) { - var spec = specs[i]; - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); - if (specFilter(spec)) { - this.runningSpecCount++; - } - } - }; - - this.specComplete = function(spec) { - this.completeSpecCount++; - - if (isUndefined(this.views.specs[spec.id])) { - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); - } - - var specView = this.views.specs[spec.id]; - - switch (specView.status()) { - case 'passed': - this.passedCount++; - break; - - case 'failed': - this.failedCount++; - break; - - case 'skipped': - this.skippedCount++; - break; - } - - specView.refresh(); - this.refresh(); - }; - - this.suiteComplete = function(suite) { - var suiteView = this.views.suites[suite.id]; - if (isUndefined(suiteView)) { - return; - } - suiteView.refresh(); - }; - - this.refresh = function() { - - if (isUndefined(this.resultsMenu)) { - this.createResultsMenu(); - } - - // currently running UI - if (isUndefined(this.runningAlert)) { - this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" }); - dom.alert.appendChild(this.runningAlert); - } - this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); - - // skipped specs UI - if (isUndefined(this.skippedAlert)) { - this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" }); - } - - this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.skippedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.skippedAlert); - } - - // passing specs UI - if (isUndefined(this.passedAlert)) { - this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" }); - } - this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); - - // failing specs UI - if (isUndefined(this.failedAlert)) { - this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); - } - this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); - - if (this.failedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.failedAlert); - dom.alert.appendChild(this.resultsMenu); - } - - // summary info - this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); - this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; - }; - - this.complete = function() { - dom.alert.removeChild(this.runningAlert); - - this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.failedCount === 0) { - dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); - } else { - showDetails(); - } - - dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); - }; - - return this; - - function showDetails() { - if (dom.reporter.className.search(/showDetails/) === -1) { - dom.reporter.className += " showDetails"; - } - } - - function isUndefined(obj) { - return typeof obj === 'undefined'; - } - - function isDefined(obj) { - return !isUndefined(obj); - } - - function specPluralizedFor(count) { - var str = count + " spec"; - if (count > 1) { - str += "s" - } - return str; - } - -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); - - -jasmine.HtmlReporter.SpecView = function(spec, dom, views) { - this.spec = spec; - this.dom = dom; - this.views = views; - - this.symbol = this.createDom('li', { className: 'pending' }); - this.dom.symbolSummary.appendChild(this.symbol); - - this.summary = this.createDom('div', { className: 'specSummary' }, - this.createDom('a', { - className: 'description', - href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.description) - ); - - this.detail = this.createDom('div', { className: 'specDetail' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.getFullName()) - ); -}; - -jasmine.HtmlReporter.SpecView.prototype.status = function() { - return this.getSpecStatus(this.spec); -}; - -jasmine.HtmlReporter.SpecView.prototype.refresh = function() { - this.symbol.className = this.status(); - - switch (this.status()) { - case 'skipped': - break; - - case 'passed': - this.appendSummaryToSuiteDiv(); - break; - - case 'failed': - this.appendSummaryToSuiteDiv(); - this.appendFailureDetail(); - break; - } -}; - -jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { - this.summary.className += ' ' + this.status(); - this.appendToSummary(this.spec, this.summary); -}; - -jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { - this.detail.className += ' ' + this.status(); - - var resultItems = this.spec.results().getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - this.detail.appendChild(messagesDiv); - this.dom.details.appendChild(this.detail); - } -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { - this.suite = suite; - this.dom = dom; - this.views = views; - - this.element = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description) - ); - - this.appendToSummary(this.suite, this.element); -}; - -jasmine.HtmlReporter.SuiteView.prototype.status = function() { - return this.getSpecStatus(this.suite); -}; - -jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { - this.element.className += " " + this.status(); -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); - -/* @deprecated Use jasmine.HtmlReporter instead - */ -jasmine.TrivialReporter = function(doc) { - this.document = doc || document; - this.suiteDivs = {}; - this.logRunningSpecs = false; -}; - -jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { el.appendChild(child); } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { - var showPassed, showSkipped; - - this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, - this.createDom('div', { className: 'banner' }, - this.createDom('div', { className: 'logo' }, - this.createDom('span', { className: 'title' }, "Jasmine"), - this.createDom('span', { className: 'version' }, runner.env.versionString())), - this.createDom('div', { className: 'options' }, - "Show ", - showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), - showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") - ) - ), - - this.runnerDiv = this.createDom('div', { className: 'runner running' }, - this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), - this.runnerMessageSpan = this.createDom('span', {}, "Running..."), - this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) - ); - - this.document.body.appendChild(this.outerDiv); - - var suites = runner.suites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - var suiteDiv = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); - this.suiteDivs[suite.id] = suiteDiv; - var parentDiv = this.outerDiv; - if (suite.parentSuite) { - parentDiv = this.suiteDivs[suite.parentSuite.id]; - } - parentDiv.appendChild(suiteDiv); - } - - this.startedAt = new Date(); - - var self = this; - showPassed.onclick = function(evt) { - if (showPassed.checked) { - self.outerDiv.className += ' show-passed'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); - } - }; - - showSkipped.onclick = function(evt) { - if (showSkipped.checked) { - self.outerDiv.className += ' show-skipped'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); - } - }; -}; - -jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { - var results = runner.results(); - var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; - this.runnerDiv.setAttribute("class", className); - //do it twice for IE - this.runnerDiv.setAttribute("className", className); - var specs = runner.specs(); - var specCount = 0; - for (var i = 0; i < specs.length; i++) { - if (this.specFilter(specs[i])) { - specCount++; - } - } - var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); - message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; - this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); - - this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); -}; - -jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { - var results = suite.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.totalCount === 0) { // todo: change this to check results.skipped - status = 'skipped'; - } - this.suiteDivs[suite.id].className += " " + status; -}; - -jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { - if (this.logRunningSpecs) { - this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } -}; - -jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { - var results = spec.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - var specDiv = this.createDom('div', { className: 'spec ' + status }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(spec.getFullName()), - title: spec.getFullName() - }, spec.description)); - - - var resultItems = results.getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - specDiv.appendChild(messagesDiv); - } - - this.suiteDivs[spec.suite.id].appendChild(specDiv); -}; - -jasmine.TrivialReporter.prototype.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } -}; - -jasmine.TrivialReporter.prototype.getLocation = function() { - return this.document.location; -}; - -jasmine.TrivialReporter.prototype.specFilter = function(spec) { - var paramMap = {}; - var params = this.getLocation().search.substring(1).split('&'); - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - if (!paramMap.spec) { - return true; - } - return spec.getFullName().indexOf(paramMap.spec) === 0; -}; diff --git a/test/browser/jasmine.css b/test/browser/jasmine.css deleted file mode 100644 index 8c008dc72..000000000 --- a/test/browser/jasmine.css +++ /dev/null @@ -1,82 +0,0 @@ -body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } - -#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } -#HTMLReporter a { text-decoration: none; } -#HTMLReporter a:hover { text-decoration: underline; } -#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } -#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } -#HTMLReporter #jasmine_content { position: fixed; right: 100%; } -#HTMLReporter .version { color: #aaaaaa; } -#HTMLReporter .banner { margin-top: 14px; } -#HTMLReporter .duration { color: #aaaaaa; float: right; } -#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } -#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } -#HTMLReporter .symbolSummary li.passed { font-size: 14px; } -#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } -#HTMLReporter .symbolSummary li.failed { line-height: 9px; } -#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } -#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } -#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } -#HTMLReporter .symbolSummary li.pending { line-height: 11px; } -#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } -#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } -#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } -#HTMLReporter .runningAlert { background-color: #666666; } -#HTMLReporter .skippedAlert { background-color: #aaaaaa; } -#HTMLReporter .skippedAlert:first-child { background-color: #333333; } -#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } -#HTMLReporter .passingAlert { background-color: #a6b779; } -#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } -#HTMLReporter .failingAlert { background-color: #cf867e; } -#HTMLReporter .failingAlert:first-child { background-color: #b03911; } -#HTMLReporter .results { margin-top: 14px; } -#HTMLReporter #details { display: none; } -#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } -#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter.showDetails .summary { display: none; } -#HTMLReporter.showDetails #details { display: block; } -#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter .summary { margin-top: 14px; } -#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } -#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } -#HTMLReporter .summary .specSummary.failed a { color: #b03911; } -#HTMLReporter .description + .suite { margin-top: 0; } -#HTMLReporter .suite { margin-top: 14px; } -#HTMLReporter .suite a { color: #333333; } -#HTMLReporter #details .specDetail { margin-bottom: 28px; } -#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } -#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } -#HTMLReporter .resultMessage span.result { display: block; } -#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } - -#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } -#TrivialReporter a:visited, #TrivialReporter a { color: #303; } -#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } -#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } -#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } -#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } -#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } -#TrivialReporter .runner.running { background-color: yellow; } -#TrivialReporter .options { text-align: right; font-size: .8em; } -#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } -#TrivialReporter .suite .suite { margin: 5px; } -#TrivialReporter .suite.passed { background-color: #dfd; } -#TrivialReporter .suite.failed { background-color: #fdd; } -#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } -#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } -#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } -#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } -#TrivialReporter .spec.skipped { background-color: #bbb; } -#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } -#TrivialReporter .passed { background-color: #cfc; display: none; } -#TrivialReporter .failed { background-color: #fbb; } -#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } -#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } -#TrivialReporter .resultMessage .mismatch { color: black; } -#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } -#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } -#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } -#TrivialReporter #jasmine_content { position: fixed; right: 100%; } -#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/test/browser/jasmine.js b/test/browser/jasmine.js deleted file mode 100644 index 019efdb57..000000000 --- a/test/browser/jasmine.js +++ /dev/null @@ -1,2600 +0,0 @@ -var isCommonJS = typeof window == "undefined" && typeof exports == "object"; - -/** - * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. - * - * @namespace - */ -var jasmine = {}; -if (isCommonJS) exports.jasmine = jasmine; -/** - * @private - */ -jasmine.unimplementedMethod_ = function() { - throw new Error("unimplemented method"); -}; - -/** - * Use jasmine.undefined instead of undefined, since undefined is just - * a plain old variable and may be redefined by somebody else. - * - * @private - */ -jasmine.undefined = jasmine.___undefined___; - -/** - * Show diagnostic messages in the console if set to true - * - */ -jasmine.VERBOSE = false; - -/** - * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. - * - */ -jasmine.DEFAULT_UPDATE_INTERVAL = 250; - -/** - * Maximum levels of nesting that will be included when an object is pretty-printed - */ -jasmine.MAX_PRETTY_PRINT_DEPTH = 40; - -/** - * Default timeout interval in milliseconds for waitsFor() blocks. - */ -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; - -/** - * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. - * Set to false to let the exception bubble up in the browser. - * - */ -jasmine.CATCH_EXCEPTIONS = true; - -jasmine.getGlobal = function() { - function getGlobal() { - return this; - } - - return getGlobal(); -}; - -/** - * Allows for bound functions to be compared. Internal use only. - * - * @ignore - * @private - * @param base {Object} bound 'this' for the function - * @param name {Function} function to find - */ -jasmine.bindOriginal_ = function(base, name) { - var original = base[name]; - if (original.apply) { - return function() { - return original.apply(base, arguments); - }; - } else { - // IE support - return jasmine.getGlobal()[name]; - } -}; - -jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); -jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); -jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); -jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); - -jasmine.MessageResult = function(values) { - this.type = 'log'; - this.values = values; - this.trace = new Error(); // todo: test better -}; - -jasmine.MessageResult.prototype.toString = function() { - var text = ""; - for (var i = 0; i < this.values.length; i++) { - if (i > 0) text += " "; - if (jasmine.isString_(this.values[i])) { - text += this.values[i]; - } else { - text += jasmine.pp(this.values[i]); - } - } - return text; -}; - -jasmine.ExpectationResult = function(params) { - this.type = 'expect'; - this.matcherName = params.matcherName; - this.passed_ = params.passed; - this.expected = params.expected; - this.actual = params.actual; - this.message = this.passed_ ? 'Passed.' : params.message; - - var trace = (params.trace || new Error(this.message)); - this.trace = this.passed_ ? '' : trace; -}; - -jasmine.ExpectationResult.prototype.toString = function () { - return this.message; -}; - -jasmine.ExpectationResult.prototype.passed = function () { - return this.passed_; -}; - -/** - * Getter for the Jasmine environment. Ensures one gets created - */ -jasmine.getEnv = function() { - var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); - return env; -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isArray_ = function(value) { - return jasmine.isA_("Array", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isString_ = function(value) { - return jasmine.isA_("String", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isNumber_ = function(value) { - return jasmine.isA_("Number", value); -}; - -/** - * @ignore - * @private - * @param {String} typeName - * @param value - * @returns {Boolean} - */ -jasmine.isA_ = function(typeName, value) { - return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; -}; - -/** - * Pretty printer for expecations. Takes any object and turns it into a human-readable string. - * - * @param value {Object} an object to be outputted - * @returns {String} - */ -jasmine.pp = function(value) { - var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); - stringPrettyPrinter.format(value); - return stringPrettyPrinter.string; -}; - -/** - * Returns true if the object is a DOM Node. - * - * @param {Object} obj object to check - * @returns {Boolean} - */ -jasmine.isDomNode = function(obj) { - return obj.nodeType > 0; -}; - -/** - * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. - * - * @example - * // don't care about which function is passed in, as long as it's a function - * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); - * - * @param {Class} clazz - * @returns matchable object of the type clazz - */ -jasmine.any = function(clazz) { - return new jasmine.Matchers.Any(clazz); -}; - -/** - * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the - * attributes on the object. - * - * @example - * // don't care about any other attributes than foo. - * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); - * - * @param sample {Object} sample - * @returns matchable object for the sample - */ -jasmine.objectContaining = function (sample) { - return new jasmine.Matchers.ObjectContaining(sample); -}; - -/** - * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. - * - * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine - * expectation syntax. Spies can be checked if they were called or not and what the calling params were. - * - * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). - * - * Spies are torn down at the end of every spec. - * - * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. - * - * @example - * // a stub - * var myStub = jasmine.createSpy('myStub'); // can be used anywhere - * - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // actual foo.not will not be called, execution stops - * spyOn(foo, 'not'); - - // foo.not spied upon, execution will continue to implementation - * spyOn(foo, 'not').andCallThrough(); - * - * // fake example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // foo.not(val) will return val - * spyOn(foo, 'not').andCallFake(function(value) {return value;}); - * - * // mock example - * foo.not(7 == 7); - * expect(foo.not).toHaveBeenCalled(); - * expect(foo.not).toHaveBeenCalledWith(true); - * - * @constructor - * @see spyOn, jasmine.createSpy, jasmine.createSpyObj - * @param {String} name - */ -jasmine.Spy = function(name) { - /** - * The name of the spy, if provided. - */ - this.identity = name || 'unknown'; - /** - * Is this Object a spy? - */ - this.isSpy = true; - /** - * The actual function this spy stubs. - */ - this.plan = function() { - }; - /** - * Tracking of the most recent call to the spy. - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy.mostRecentCall.args = [1, 2]; - */ - this.mostRecentCall = {}; - - /** - * Holds arguments for each call to the spy, indexed by call count - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy(7, 8); - * mySpy.mostRecentCall.args = [7, 8]; - * mySpy.argsForCall[0] = [1, 2]; - * mySpy.argsForCall[1] = [7, 8]; - */ - this.argsForCall = []; - this.calls = []; -}; - -/** - * Tells a spy to call through to the actual implemenatation. - * - * @example - * var foo = { - * bar: function() { // do some stuff } - * } - * - * // defining a spy on an existing property: foo.bar - * spyOn(foo, 'bar').andCallThrough(); - */ -jasmine.Spy.prototype.andCallThrough = function() { - this.plan = this.originalValue; - return this; -}; - -/** - * For setting the return value of a spy. - * - * @example - * // defining a spy from scratch: foo() returns 'baz' - * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); - * - * // defining a spy on an existing property: foo.bar() returns 'baz' - * spyOn(foo, 'bar').andReturn('baz'); - * - * @param {Object} value - */ -jasmine.Spy.prototype.andReturn = function(value) { - this.plan = function() { - return value; - }; - return this; -}; - -/** - * For throwing an exception when a spy is called. - * - * @example - * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' - * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); - * - * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' - * spyOn(foo, 'bar').andThrow('baz'); - * - * @param {String} exceptionMsg - */ -jasmine.Spy.prototype.andThrow = function(exceptionMsg) { - this.plan = function() { - throw exceptionMsg; - }; - return this; -}; - -/** - * Calls an alternate implementation when a spy is called. - * - * @example - * var baz = function() { - * // do some stuff, return something - * } - * // defining a spy from scratch: foo() calls the function baz - * var foo = jasmine.createSpy('spy on foo').andCall(baz); - * - * // defining a spy on an existing property: foo.bar() calls an anonymnous function - * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); - * - * @param {Function} fakeFunc - */ -jasmine.Spy.prototype.andCallFake = function(fakeFunc) { - this.plan = fakeFunc; - return this; -}; - -/** - * Resets all of a spy's the tracking variables so that it can be used again. - * - * @example - * spyOn(foo, 'bar'); - * - * foo.bar(); - * - * expect(foo.bar.callCount).toEqual(1); - * - * foo.bar.reset(); - * - * expect(foo.bar.callCount).toEqual(0); - */ -jasmine.Spy.prototype.reset = function() { - this.wasCalled = false; - this.callCount = 0; - this.argsForCall = []; - this.calls = []; - this.mostRecentCall = {}; -}; - -jasmine.createSpy = function(name) { - - var spyObj = function() { - spyObj.wasCalled = true; - spyObj.callCount++; - var args = jasmine.util.argsToArray(arguments); - spyObj.mostRecentCall.object = this; - spyObj.mostRecentCall.args = args; - spyObj.argsForCall.push(args); - spyObj.calls.push({object: this, args: args}); - return spyObj.plan.apply(this, arguments); - }; - - var spy = new jasmine.Spy(name); - - for (var prop in spy) { - spyObj[prop] = spy[prop]; - } - - spyObj.reset(); - - return spyObj; -}; - -/** - * Determines whether an object is a spy. - * - * @param {jasmine.Spy|Object} putativeSpy - * @returns {Boolean} - */ -jasmine.isSpy = function(putativeSpy) { - return putativeSpy && putativeSpy.isSpy; -}; - -/** - * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something - * large in one call. - * - * @param {String} baseName name of spy class - * @param {Array} methodNames array of names of methods to make spies - */ -jasmine.createSpyObj = function(baseName, methodNames) { - if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { - throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); - } - var obj = {}; - for (var i = 0; i < methodNames.length; i++) { - obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); - } - return obj; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the current spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.log = function() { - var spec = jasmine.getEnv().currentSpec; - spec.log.apply(spec, arguments); -}; - -/** - * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. - * - * @example - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops - * - * @see jasmine.createSpy - * @param obj - * @param methodName - * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods - */ -var spyOn = function(obj, methodName) { - return jasmine.getEnv().currentSpec.spyOn(obj, methodName); -}; -if (isCommonJS) exports.spyOn = spyOn; - -/** - * Creates a Jasmine spec that will be added to the current suite. - * - * // TODO: pending tests - * - * @example - * it('should be true', function() { - * expect(true).toEqual(true); - * }); - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var it = function(desc, func) { - return jasmine.getEnv().it(desc, func); -}; -if (isCommonJS) exports.it = it; - -/** - * Creates a disabled Jasmine spec. - * - * A convenience method that allows existing specs to be disabled temporarily during development. - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var xit = function(desc, func) { - return jasmine.getEnv().xit(desc, func); -}; -if (isCommonJS) exports.xit = xit; - -/** - * Starts a chain for a Jasmine expectation. - * - * It is passed an Object that is the actual value and should chain to one of the many - * jasmine.Matchers functions. - * - * @param {Object} actual Actual value to test against and expected value - * @return {jasmine.Matchers} - */ -var expect = function(actual) { - return jasmine.getEnv().currentSpec.expect(actual); -}; -if (isCommonJS) exports.expect = expect; - -/** - * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. - * - * @param {Function} func Function that defines part of a jasmine spec. - */ -var runs = function(func) { - jasmine.getEnv().currentSpec.runs(func); -}; -if (isCommonJS) exports.runs = runs; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -var waits = function(timeout) { - jasmine.getEnv().currentSpec.waits(timeout); -}; -if (isCommonJS) exports.waits = waits; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); -}; -if (isCommonJS) exports.waitsFor = waitsFor; - -/** - * A function that is called before each spec in a suite. - * - * Used for spec setup, including validating assumptions. - * - * @param {Function} beforeEachFunction - */ -var beforeEach = function(beforeEachFunction) { - jasmine.getEnv().beforeEach(beforeEachFunction); -}; -if (isCommonJS) exports.beforeEach = beforeEach; - -/** - * A function that is called after each spec in a suite. - * - * Used for restoring any state that is hijacked during spec execution. - * - * @param {Function} afterEachFunction - */ -var afterEach = function(afterEachFunction) { - jasmine.getEnv().afterEach(afterEachFunction); -}; -if (isCommonJS) exports.afterEach = afterEach; - -/** - * Defines a suite of specifications. - * - * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared - * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization - * of setup in some tests. - * - * @example - * // TODO: a simple suite - * - * // TODO: a simple suite with a nested describe block - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var describe = function(description, specDefinitions) { - return jasmine.getEnv().describe(description, specDefinitions); -}; -if (isCommonJS) exports.describe = describe; - -/** - * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var xdescribe = function(description, specDefinitions) { - return jasmine.getEnv().xdescribe(description, specDefinitions); -}; -if (isCommonJS) exports.xdescribe = xdescribe; - - -// Provide the XMLHttpRequest class for IE 5.x-6.x: -jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { - function tryIt(f) { - try { - return f(); - } catch(e) { - } - return null; - } - - var xhr = tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.6.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.3.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP"); - }) || - tryIt(function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }); - - if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); - - return xhr; -} : XMLHttpRequest; -/** - * @namespace - */ -jasmine.util = {}; - -/** - * Declare that a child class inherit it's prototype from the parent class. - * - * @private - * @param {Function} childClass - * @param {Function} parentClass - */ -jasmine.util.inherit = function(childClass, parentClass) { - /** - * @private - */ - var subclass = function() { - }; - subclass.prototype = parentClass.prototype; - childClass.prototype = new subclass(); -}; - -jasmine.util.formatException = function(e) { - var lineNumber; - if (e.line) { - lineNumber = e.line; - } - else if (e.lineNumber) { - lineNumber = e.lineNumber; - } - - var file; - - if (e.sourceURL) { - file = e.sourceURL; - } - else if (e.fileName) { - file = e.fileName; - } - - var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); - - if (file && lineNumber) { - message += ' in ' + file + ' (line ' + lineNumber + ')'; - } - - return message; -}; - -jasmine.util.htmlEscape = function(str) { - if (!str) return str; - return str.replace(/&/g, '&') - .replace(//g, '>'); -}; - -jasmine.util.argsToArray = function(args) { - var arrayOfArgs = []; - for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); - return arrayOfArgs; -}; - -jasmine.util.extend = function(destination, source) { - for (var property in source) destination[property] = source[property]; - return destination; -}; - -/** - * Environment for Jasmine - * - * @constructor - */ -jasmine.Env = function() { - this.currentSpec = null; - this.currentSuite = null; - this.currentRunner_ = new jasmine.Runner(this); - - this.reporter = new jasmine.MultiReporter(); - - this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; - this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; - this.lastUpdate = 0; - this.specFilter = function() { - return true; - }; - - this.nextSpecId_ = 0; - this.nextSuiteId_ = 0; - this.equalityTesters_ = []; - - // wrap matchers - this.matchersClass = function() { - jasmine.Matchers.apply(this, arguments); - }; - jasmine.util.inherit(this.matchersClass, jasmine.Matchers); - - jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); -}; - - -jasmine.Env.prototype.setTimeout = jasmine.setTimeout; -jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; -jasmine.Env.prototype.setInterval = jasmine.setInterval; -jasmine.Env.prototype.clearInterval = jasmine.clearInterval; - -/** - * @returns an object containing jasmine version build info, if set. - */ -jasmine.Env.prototype.version = function () { - if (jasmine.version_) { - return jasmine.version_; - } else { - throw new Error('Version not set'); - } -}; - -/** - * @returns string containing jasmine version build info, if set. - */ -jasmine.Env.prototype.versionString = function() { - if (!jasmine.version_) { - return "version unknown"; - } - - var version = this.version(); - var versionString = version.major + "." + version.minor + "." + version.build; - if (version.release_candidate) { - versionString += ".rc" + version.release_candidate; - } - versionString += " revision " + version.revision; - return versionString; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSpecId = function () { - return this.nextSpecId_++; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSuiteId = function () { - return this.nextSuiteId_++; -}; - -/** - * Register a reporter to receive status updates from Jasmine. - * @param {jasmine.Reporter} reporter An object which will receive status updates. - */ -jasmine.Env.prototype.addReporter = function(reporter) { - this.reporter.addReporter(reporter); -}; - -jasmine.Env.prototype.execute = function() { - this.currentRunner_.execute(); -}; - -jasmine.Env.prototype.describe = function(description, specDefinitions) { - var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); - - var parentSuite = this.currentSuite; - if (parentSuite) { - parentSuite.add(suite); - } else { - this.currentRunner_.add(suite); - } - - this.currentSuite = suite; - - var declarationError = null; - try { - specDefinitions.call(suite); - } catch(e) { - declarationError = e; - } - - if (declarationError) { - this.it("encountered a declaration exception", function() { - throw declarationError; - }); - } - - this.currentSuite = parentSuite; - - return suite; -}; - -jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { - if (this.currentSuite) { - this.currentSuite.beforeEach(beforeEachFunction); - } else { - this.currentRunner_.beforeEach(beforeEachFunction); - } -}; - -jasmine.Env.prototype.currentRunner = function () { - return this.currentRunner_; -}; - -jasmine.Env.prototype.afterEach = function(afterEachFunction) { - if (this.currentSuite) { - this.currentSuite.afterEach(afterEachFunction); - } else { - this.currentRunner_.afterEach(afterEachFunction); - } - -}; - -jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { - return { - execute: function() { - } - }; -}; - -jasmine.Env.prototype.it = function(description, func) { - var spec = new jasmine.Spec(this, this.currentSuite, description); - this.currentSuite.add(spec); - this.currentSpec = spec; - - if (func) { - spec.runs(func); - } - - return spec; -}; - -jasmine.Env.prototype.xit = function(desc, func) { - return { - id: this.nextSpecId(), - runs: function() { - } - }; -}; - -jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.source != b.source) - mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); - - if (a.ignoreCase != b.ignoreCase) - mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); - - if (a.global != b.global) - mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); - - if (a.multiline != b.multiline) - mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); - - if (a.sticky != b.sticky) - mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); - - return (mismatchValues.length === 0); -}; - -jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { - return true; - } - - a.__Jasmine_been_here_before__ = b; - b.__Jasmine_been_here_before__ = a; - - var hasKey = function(obj, keyName) { - return obj !== null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in b) { - if (!hasKey(a, property) && hasKey(b, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - } - for (property in a) { - if (!hasKey(b, property) && hasKey(a, property)) { - mismatchKeys.push("expected missing key '" + property + "', but present in actual."); - } - } - for (property in b) { - if (property == '__Jasmine_been_here_before__') continue; - if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); - } - } - - if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { - mismatchValues.push("arrays were not the same length"); - } - - delete a.__Jasmine_been_here_before__; - delete b.__Jasmine_been_here_before__; - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - for (var i = 0; i < this.equalityTesters_.length; i++) { - var equalityTester = this.equalityTesters_[i]; - var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); - if (result !== jasmine.undefined) return result; - } - - if (a === b) return true; - - if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { - return (a == jasmine.undefined && b == jasmine.undefined); - } - - if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { - return a === b; - } - - if (a instanceof Date && b instanceof Date) { - return a.getTime() == b.getTime(); - } - - if (a.jasmineMatches) { - return a.jasmineMatches(b); - } - - if (b.jasmineMatches) { - return b.jasmineMatches(a); - } - - if (a instanceof jasmine.Matchers.ObjectContaining) { - return a.matches(b); - } - - if (b instanceof jasmine.Matchers.ObjectContaining) { - return b.matches(a); - } - - if (jasmine.isString_(a) && jasmine.isString_(b)) { - return (a == b); - } - - if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { - return (a == b); - } - - if (a instanceof RegExp && b instanceof RegExp) { - return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); - } - - if (typeof a === "object" && typeof b === "object") { - return this.compareObjects_(a, b, mismatchKeys, mismatchValues); - } - - //Straight check - return (a === b); -}; - -jasmine.Env.prototype.contains_ = function(haystack, needle) { - if (jasmine.isArray_(haystack)) { - for (var i = 0; i < haystack.length; i++) { - if (this.equals_(haystack[i], needle)) return true; - } - return false; - } - return haystack.indexOf(needle) >= 0; -}; - -jasmine.Env.prototype.addEqualityTester = function(equalityTester) { - this.equalityTesters_.push(equalityTester); -}; -/** No-op base class for Jasmine reporters. - * - * @constructor - */ -jasmine.Reporter = function() { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerResults = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecStarting = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecResults = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.log = function(str) { -}; - -/** - * Blocks are functions with executable code that make up a spec. - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {jasmine.Spec} spec - */ -jasmine.Block = function(env, func, spec) { - this.env = env; - this.func = func; - this.spec = spec; -}; - -jasmine.Block.prototype.execute = function(onComplete) { - if (!jasmine.CATCH_EXCEPTIONS) { - this.func.apply(this.spec); - } - else { - try { - this.func.apply(this.spec); - } catch (e) { - this.spec.fail(e); - } - } - onComplete(); -}; -/** JavaScript API reporter. - * - * @constructor - */ -jasmine.JsApiReporter = function() { - this.started = false; - this.finished = false; - this.suites_ = []; - this.results_ = {}; -}; - -jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { - this.started = true; - var suites = runner.topLevelSuites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - this.suites_.push(this.summarize_(suite)); - } -}; - -jasmine.JsApiReporter.prototype.suites = function() { - return this.suites_; -}; - -jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { - var isSuite = suiteOrSpec instanceof jasmine.Suite; - var summary = { - id: suiteOrSpec.id, - name: suiteOrSpec.description, - type: isSuite ? 'suite' : 'spec', - children: [] - }; - - if (isSuite) { - var children = suiteOrSpec.children(); - for (var i = 0; i < children.length; i++) { - summary.children.push(this.summarize_(children[i])); - } - } - return summary; -}; - -jasmine.JsApiReporter.prototype.results = function() { - return this.results_; -}; - -jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { - return this.results_[specId]; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { - this.finished = true; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { - this.results_[spec.id] = { - messages: spec.results().getItems(), - result: spec.results().failedCount > 0 ? "failed" : "passed" - }; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.log = function(str) { -}; - -jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ - var results = {}; - for (var i = 0; i < specIds.length; i++) { - var specId = specIds[i]; - results[specId] = this.summarizeResult_(this.results_[specId]); - } - return results; -}; - -jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ - var summaryMessages = []; - var messagesLength = result.messages.length; - for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { - var resultMessage = result.messages[messageIndex]; - summaryMessages.push({ - text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, - passed: resultMessage.passed ? resultMessage.passed() : true, - type: resultMessage.type, - message: resultMessage.message, - trace: { - stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined - } - }); - } - - return { - result : result.result, - messages : summaryMessages - }; -}; - -/** - * @constructor - * @param {jasmine.Env} env - * @param actual - * @param {jasmine.Spec} spec - */ -jasmine.Matchers = function(env, actual, spec, opt_isNot) { - this.env = env; - this.actual = actual; - this.spec = spec; - this.isNot = opt_isNot || false; - this.reportWasCalled_ = false; -}; - -// todo: @deprecated as of Jasmine 0.11, remove soon [xw] -jasmine.Matchers.pp = function(str) { - throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); -}; - -// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] -jasmine.Matchers.prototype.report = function(result, failing_message, details) { - throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); -}; - -jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { - for (var methodName in prototype) { - if (methodName == 'report') continue; - var orig = prototype[methodName]; - matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); - } -}; - -jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { - return function() { - var matcherArgs = jasmine.util.argsToArray(arguments); - var result = matcherFunction.apply(this, arguments); - - if (this.isNot) { - result = !result; - } - - if (this.reportWasCalled_) return result; - - var message; - if (!result) { - if (this.message) { - message = this.message.apply(this, arguments); - if (jasmine.isArray_(message)) { - message = message[this.isNot ? 1 : 0]; - } - } else { - var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); - message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; - if (matcherArgs.length > 0) { - for (var i = 0; i < matcherArgs.length; i++) { - if (i > 0) message += ","; - message += " " + jasmine.pp(matcherArgs[i]); - } - } - message += "."; - } - } - var expectationResult = new jasmine.ExpectationResult({ - matcherName: matcherName, - passed: result, - expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], - actual: this.actual, - message: message - }); - this.spec.addMatcherResult(expectationResult); - return jasmine.undefined; - }; -}; - - - - -/** - * toBe: compares the actual to the expected using === - * @param expected - */ -jasmine.Matchers.prototype.toBe = function(expected) { - return this.actual === expected; -}; - -/** - * toNotBe: compares the actual to the expected using !== - * @param expected - * @deprecated as of 1.0. Use not.toBe() instead. - */ -jasmine.Matchers.prototype.toNotBe = function(expected) { - return this.actual !== expected; -}; - -/** - * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. - * - * @param expected - */ -jasmine.Matchers.prototype.toEqual = function(expected) { - return this.env.equals_(this.actual, expected); -}; - -/** - * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual - * @param expected - * @deprecated as of 1.0. Use not.toEqual() instead. - */ -jasmine.Matchers.prototype.toNotEqual = function(expected) { - return !this.env.equals_(this.actual, expected); -}; - -/** - * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes - * a pattern or a String. - * - * @param expected - */ -jasmine.Matchers.prototype.toMatch = function(expected) { - return new RegExp(expected).test(this.actual); -}; - -/** - * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch - * @param expected - * @deprecated as of 1.0. Use not.toMatch() instead. - */ -jasmine.Matchers.prototype.toNotMatch = function(expected) { - return !(new RegExp(expected).test(this.actual)); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeDefined = function() { - return (this.actual !== jasmine.undefined); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeUndefined = function() { - return (this.actual === jasmine.undefined); -}; - -/** - * Matcher that compares the actual to null. - */ -jasmine.Matchers.prototype.toBeNull = function() { - return (this.actual === null); -}; - -/** - * Matcher that compares the actual to NaN. - */ -jasmine.Matchers.prototype.toBeNaN = function() { - this.message = function() { - return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; - }; - - return (this.actual !== this.actual); -}; - -/** - * Matcher that boolean not-nots the actual. - */ -jasmine.Matchers.prototype.toBeTruthy = function() { - return !!this.actual; -}; - - -/** - * Matcher that boolean nots the actual. - */ -jasmine.Matchers.prototype.toBeFalsy = function() { - return !this.actual; -}; - - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called. - */ -jasmine.Matchers.prototype.toHaveBeenCalled = function() { - if (arguments.length > 0) { - throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to have been called.", - "Expected spy " + this.actual.identity + " not to have been called." - ]; - }; - - return this.actual.wasCalled; -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ -jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was not called. - * - * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead - */ -jasmine.Matchers.prototype.wasNotCalled = function() { - if (arguments.length > 0) { - throw new Error('wasNotCalled does not take arguments'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to not have been called.", - "Expected spy " + this.actual.identity + " to have been called." - ]; - }; - - return !this.actual.wasCalled; -}; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. - * - * @example - * - */ -jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - this.message = function() { - var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; - var positiveMessage = ""; - if (this.actual.callCount === 0) { - positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; - } else { - positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') - } - return [positiveMessage, invertedMessage]; - }; - - return this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; - -/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasNotCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", - "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" - ]; - }; - - return !this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** - * Matcher that checks that the expected item is an element in the actual Array. - * - * @param {Object} expected - */ -jasmine.Matchers.prototype.toContain = function(expected) { - return this.env.contains_(this.actual, expected); -}; - -/** - * Matcher that checks that the expected item is NOT an element in the actual Array. - * - * @param {Object} expected - * @deprecated as of 1.0. Use not.toContain() instead. - */ -jasmine.Matchers.prototype.toNotContain = function(expected) { - return !this.env.contains_(this.actual, expected); -}; - -jasmine.Matchers.prototype.toBeLessThan = function(expected) { - return this.actual < expected; -}; - -jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { - return this.actual > expected; -}; - -/** - * Matcher that checks that the expected item is equal to the actual item - * up to a given level of decimal precision (default 2). - * - * @param {Number} expected - * @param {Number} precision, as number of decimal places - */ -jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { - if (!(precision === 0)) { - precision = precision || 2; - } - return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); -}; - -/** - * Matcher that checks that the expected exception was thrown by the actual. - * - * @param {String} [expected] - */ -jasmine.Matchers.prototype.toThrow = function(expected) { - var result = false; - var exception; - if (typeof this.actual != 'function') { - throw new Error('Actual is not a function'); - } - try { - this.actual(); - } catch (e) { - exception = e; - } - if (exception) { - result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); - } - - var not = this.isNot ? "not " : ""; - - this.message = function() { - if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { - return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); - } else { - return "Expected function to throw an exception."; - } - }; - - return result; -}; - -jasmine.Matchers.Any = function(expectedClass) { - this.expectedClass = expectedClass; -}; - -jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { - if (this.expectedClass == String) { - return typeof other == 'string' || other instanceof String; - } - - if (this.expectedClass == Number) { - return typeof other == 'number' || other instanceof Number; - } - - if (this.expectedClass == Function) { - return typeof other == 'function' || other instanceof Function; - } - - if (this.expectedClass == Object) { - return typeof other == 'object'; - } - - return other instanceof this.expectedClass; -}; - -jasmine.Matchers.Any.prototype.jasmineToString = function() { - return ''; -}; - -jasmine.Matchers.ObjectContaining = function (sample) { - this.sample = sample; -}; - -jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - var env = jasmine.getEnv(); - - var hasKey = function(obj, keyName) { - return obj != null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in this.sample) { - if (!hasKey(other, property) && hasKey(this.sample, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); - } - } - - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { - return ""; -}; -// Mock setTimeout, clearTimeout -// Contributed by Pivotal Computer Systems, www.pivotalsf.com - -jasmine.FakeTimer = function() { - this.reset(); - - var self = this; - self.setTimeout = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); - return self.timeoutsMade; - }; - - self.setInterval = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); - return self.timeoutsMade; - }; - - self.clearTimeout = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - - self.clearInterval = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - -}; - -jasmine.FakeTimer.prototype.reset = function() { - this.timeoutsMade = 0; - this.scheduledFunctions = {}; - this.nowMillis = 0; -}; - -jasmine.FakeTimer.prototype.tick = function(millis) { - var oldMillis = this.nowMillis; - var newMillis = oldMillis + millis; - this.runFunctionsWithinRange(oldMillis, newMillis); - this.nowMillis = newMillis; -}; - -jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { - var scheduledFunc; - var funcsToRun = []; - for (var timeoutKey in this.scheduledFunctions) { - scheduledFunc = this.scheduledFunctions[timeoutKey]; - if (scheduledFunc != jasmine.undefined && - scheduledFunc.runAtMillis >= oldMillis && - scheduledFunc.runAtMillis <= nowMillis) { - funcsToRun.push(scheduledFunc); - this.scheduledFunctions[timeoutKey] = jasmine.undefined; - } - } - - if (funcsToRun.length > 0) { - funcsToRun.sort(function(a, b) { - return a.runAtMillis - b.runAtMillis; - }); - for (var i = 0; i < funcsToRun.length; ++i) { - try { - var funcToRun = funcsToRun[i]; - this.nowMillis = funcToRun.runAtMillis; - funcToRun.funcToCall(); - if (funcToRun.recurring) { - this.scheduleFunction(funcToRun.timeoutKey, - funcToRun.funcToCall, - funcToRun.millis, - true); - } - } catch(e) { - } - } - this.runFunctionsWithinRange(oldMillis, nowMillis); - } -}; - -jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { - this.scheduledFunctions[timeoutKey] = { - runAtMillis: this.nowMillis + millis, - funcToCall: funcToCall, - recurring: recurring, - timeoutKey: timeoutKey, - millis: millis - }; -}; - -/** - * @namespace - */ -jasmine.Clock = { - defaultFakeTimer: new jasmine.FakeTimer(), - - reset: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.reset(); - }, - - tick: function(millis) { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.tick(millis); - }, - - runFunctionsWithinRange: function(oldMillis, nowMillis) { - jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); - }, - - scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { - jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); - }, - - useMock: function() { - if (!jasmine.Clock.isInstalled()) { - var spec = jasmine.getEnv().currentSpec; - spec.after(jasmine.Clock.uninstallMock); - - jasmine.Clock.installMock(); - } - }, - - installMock: function() { - jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; - }, - - uninstallMock: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.installed = jasmine.Clock.real; - }, - - real: { - setTimeout: jasmine.getGlobal().setTimeout, - clearTimeout: jasmine.getGlobal().clearTimeout, - setInterval: jasmine.getGlobal().setInterval, - clearInterval: jasmine.getGlobal().clearInterval - }, - - assertInstalled: function() { - if (!jasmine.Clock.isInstalled()) { - throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); - } - }, - - isInstalled: function() { - return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; - }, - - installed: null -}; -jasmine.Clock.installed = jasmine.Clock.real; - -//else for IE support -jasmine.getGlobal().setTimeout = function(funcToCall, millis) { - if (jasmine.Clock.installed.setTimeout.apply) { - return jasmine.Clock.installed.setTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.setTimeout(funcToCall, millis); - } -}; - -jasmine.getGlobal().setInterval = function(funcToCall, millis) { - if (jasmine.Clock.installed.setInterval.apply) { - return jasmine.Clock.installed.setInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.setInterval(funcToCall, millis); - } -}; - -jasmine.getGlobal().clearTimeout = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearTimeout(timeoutKey); - } -}; - -jasmine.getGlobal().clearInterval = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearInterval(timeoutKey); - } -}; - -/** - * @constructor - */ -jasmine.MultiReporter = function() { - this.subReporters_ = []; -}; -jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); - -jasmine.MultiReporter.prototype.addReporter = function(reporter) { - this.subReporters_.push(reporter); -}; - -(function() { - var functionNames = [ - "reportRunnerStarting", - "reportRunnerResults", - "reportSuiteResults", - "reportSpecStarting", - "reportSpecResults", - "log" - ]; - for (var i = 0; i < functionNames.length; i++) { - var functionName = functionNames[i]; - jasmine.MultiReporter.prototype[functionName] = (function(functionName) { - return function() { - for (var j = 0; j < this.subReporters_.length; j++) { - var subReporter = this.subReporters_[j]; - if (subReporter[functionName]) { - subReporter[functionName].apply(subReporter, arguments); - } - } - }; - })(functionName); - } -})(); -/** - * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults - * - * @constructor - */ -jasmine.NestedResults = function() { - /** - * The total count of results - */ - this.totalCount = 0; - /** - * Number of passed results - */ - this.passedCount = 0; - /** - * Number of failed results - */ - this.failedCount = 0; - /** - * Was this suite/spec skipped? - */ - this.skipped = false; - /** - * @ignore - */ - this.items_ = []; -}; - -/** - * Roll up the result counts. - * - * @param result - */ -jasmine.NestedResults.prototype.rollupCounts = function(result) { - this.totalCount += result.totalCount; - this.passedCount += result.passedCount; - this.failedCount += result.failedCount; -}; - -/** - * Adds a log message. - * @param values Array of message parts which will be concatenated later. - */ -jasmine.NestedResults.prototype.log = function(values) { - this.items_.push(new jasmine.MessageResult(values)); -}; - -/** - * Getter for the results: message & results. - */ -jasmine.NestedResults.prototype.getItems = function() { - return this.items_; -}; - -/** - * Adds a result, tracking counts (total, passed, & failed) - * @param {jasmine.ExpectationResult|jasmine.NestedResults} result - */ -jasmine.NestedResults.prototype.addResult = function(result) { - if (result.type != 'log') { - if (result.items_) { - this.rollupCounts(result); - } else { - this.totalCount++; - if (result.passed()) { - this.passedCount++; - } else { - this.failedCount++; - } - } - } - this.items_.push(result); -}; - -/** - * @returns {Boolean} True if everything below passed - */ -jasmine.NestedResults.prototype.passed = function() { - return this.passedCount === this.totalCount; -}; -/** - * Base class for pretty printing for expectation results. - */ -jasmine.PrettyPrinter = function() { - this.ppNestLevel_ = 0; -}; - -/** - * Formats a value in a nice, human-readable string. - * - * @param value - */ -jasmine.PrettyPrinter.prototype.format = function(value) { - this.ppNestLevel_++; - try { - if (value === jasmine.undefined) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === jasmine.getGlobal()) { - this.emitScalar(''); - } else if (value.jasmineToString) { - this.emitScalar(value.jasmineToString()); - } else if (typeof value === 'string') { - this.emitString(value); - } else if (jasmine.isSpy(value)) { - this.emitScalar("spy on " + value.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (typeof value.nodeType === 'number') { - this.emitScalar('HTMLNode'); - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (value.__Jasmine_been_here_before__) { - this.emitScalar(''); - } else if (jasmine.isArray_(value) || typeof value == 'object') { - value.__Jasmine_been_here_before__ = true; - if (jasmine.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - delete value.__Jasmine_been_here_before__; - } else { - this.emitScalar(value.toString()); - } - } finally { - this.ppNestLevel_--; - } -}; - -jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { - for (var property in obj) { - if (!obj.hasOwnProperty(property)) continue; - if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && - obj.__lookupGetter__(property) !== null) : false); - } -}; - -jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; - -jasmine.StringPrettyPrinter = function() { - jasmine.PrettyPrinter.call(this); - - this.string = ''; -}; -jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); - -jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { - this.append(value); -}; - -jasmine.StringPrettyPrinter.prototype.emitString = function(value) { - this.append("'" + value + "'"); -}; - -jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { - if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { - this.append("Array"); - return; - } - - this.append('[ '); - for (var i = 0; i < array.length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - this.append(' ]'); -}; - -jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { - if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { - this.append("Object"); - return; - } - - var self = this; - this.append('{ '); - var first = true; - - this.iterateObject(obj, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.append(property); - self.append(' : '); - if (isGetter) { - self.append(''); - } else { - self.format(obj[property]); - } - }); - - this.append(' }'); -}; - -jasmine.StringPrettyPrinter.prototype.append = function(value) { - this.string += value; -}; -jasmine.Queue = function(env) { - this.env = env; - - // parallel to blocks. each true value in this array means the block will - // get executed even if we abort - this.ensured = []; - this.blocks = []; - this.running = false; - this.index = 0; - this.offset = 0; - this.abort = false; -}; - -jasmine.Queue.prototype.addBefore = function(block, ensure) { - if (ensure === jasmine.undefined) { - ensure = false; - } - - this.blocks.unshift(block); - this.ensured.unshift(ensure); -}; - -jasmine.Queue.prototype.add = function(block, ensure) { - if (ensure === jasmine.undefined) { - ensure = false; - } - - this.blocks.push(block); - this.ensured.push(ensure); -}; - -jasmine.Queue.prototype.insertNext = function(block, ensure) { - if (ensure === jasmine.undefined) { - ensure = false; - } - - this.ensured.splice((this.index + this.offset + 1), 0, ensure); - this.blocks.splice((this.index + this.offset + 1), 0, block); - this.offset++; -}; - -jasmine.Queue.prototype.start = function(onComplete) { - this.running = true; - this.onComplete = onComplete; - this.next_(); -}; - -jasmine.Queue.prototype.isRunning = function() { - return this.running; -}; - -jasmine.Queue.LOOP_DONT_RECURSE = true; - -jasmine.Queue.prototype.next_ = function() { - var self = this; - var goAgain = true; - - while (goAgain) { - goAgain = false; - - if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { - var calledSynchronously = true; - var completedSynchronously = false; - - var onComplete = function () { - if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { - completedSynchronously = true; - return; - } - - if (self.blocks[self.index].abort) { - self.abort = true; - } - - self.offset = 0; - self.index++; - - var now = new Date().getTime(); - if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { - self.env.lastUpdate = now; - self.env.setTimeout(function() { - self.next_(); - }, 0); - } else { - if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { - goAgain = true; - } else { - self.next_(); - } - } - }; - self.blocks[self.index].execute(onComplete); - - calledSynchronously = false; - if (completedSynchronously) { - onComplete(); - } - - } else { - self.running = false; - if (self.onComplete) { - self.onComplete(); - } - } - } -}; - -jasmine.Queue.prototype.results = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.blocks.length; i++) { - if (this.blocks[i].results) { - results.addResult(this.blocks[i].results()); - } - } - return results; -}; - - -/** - * Runner - * - * @constructor - * @param {jasmine.Env} env - */ -jasmine.Runner = function(env) { - var self = this; - self.env = env; - self.queue = new jasmine.Queue(env); - self.before_ = []; - self.after_ = []; - self.suites_ = []; -}; - -jasmine.Runner.prototype.execute = function() { - var self = this; - if (self.env.reporter.reportRunnerStarting) { - self.env.reporter.reportRunnerStarting(this); - } - self.queue.start(function () { - self.finishCallback(); - }); -}; - -jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.splice(0,0,beforeEachFunction); -}; - -jasmine.Runner.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.splice(0,0,afterEachFunction); -}; - - -jasmine.Runner.prototype.finishCallback = function() { - this.env.reporter.reportRunnerResults(this); -}; - -jasmine.Runner.prototype.addSuite = function(suite) { - this.suites_.push(suite); -}; - -jasmine.Runner.prototype.add = function(block) { - if (block instanceof jasmine.Suite) { - this.addSuite(block); - } - this.queue.add(block); -}; - -jasmine.Runner.prototype.specs = function () { - var suites = this.suites(); - var specs = []; - for (var i = 0; i < suites.length; i++) { - specs = specs.concat(suites[i].specs()); - } - return specs; -}; - -jasmine.Runner.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Runner.prototype.topLevelSuites = function() { - var topLevelSuites = []; - for (var i = 0; i < this.suites_.length; i++) { - if (!this.suites_[i].parentSuite) { - topLevelSuites.push(this.suites_[i]); - } - } - return topLevelSuites; -}; - -jasmine.Runner.prototype.results = function() { - return this.queue.results(); -}; -/** - * Internal representation of a Jasmine specification, or test. - * - * @constructor - * @param {jasmine.Env} env - * @param {jasmine.Suite} suite - * @param {String} description - */ -jasmine.Spec = function(env, suite, description) { - if (!env) { - throw new Error('jasmine.Env() required'); - } - if (!suite) { - throw new Error('jasmine.Suite() required'); - } - var spec = this; - spec.id = env.nextSpecId ? env.nextSpecId() : null; - spec.env = env; - spec.suite = suite; - spec.description = description; - spec.queue = new jasmine.Queue(env); - - spec.afterCallbacks = []; - spec.spies_ = []; - - spec.results_ = new jasmine.NestedResults(); - spec.results_.description = description; - spec.matchersClass = null; -}; - -jasmine.Spec.prototype.getFullName = function() { - return this.suite.getFullName() + ' ' + this.description + '.'; -}; - - -jasmine.Spec.prototype.results = function() { - return this.results_; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.Spec.prototype.log = function() { - return this.results_.log(arguments); -}; - -jasmine.Spec.prototype.runs = function (func) { - var block = new jasmine.Block(this.env, func, this); - this.addToQueue(block); - return this; -}; - -jasmine.Spec.prototype.addToQueue = function (block) { - if (this.queue.isRunning()) { - this.queue.insertNext(block); - } else { - this.queue.add(block); - } -}; - -/** - * @param {jasmine.ExpectationResult} result - */ -jasmine.Spec.prototype.addMatcherResult = function(result) { - this.results_.addResult(result); -}; - -jasmine.Spec.prototype.expect = function(actual) { - var positive = new (this.getMatchersClass_())(this.env, actual, this); - positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); - return positive; -}; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -jasmine.Spec.prototype.waits = function(timeout) { - var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); - this.addToQueue(waitsFunc); - return this; -}; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - var latchFunction_ = null; - var optional_timeoutMessage_ = null; - var optional_timeout_ = null; - - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i]; - switch (typeof arg) { - case 'function': - latchFunction_ = arg; - break; - case 'string': - optional_timeoutMessage_ = arg; - break; - case 'number': - optional_timeout_ = arg; - break; - } - } - - var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); - this.addToQueue(waitsForFunc); - return this; -}; - -jasmine.Spec.prototype.fail = function (e) { - var expectationResult = new jasmine.ExpectationResult({ - passed: false, - message: e ? jasmine.util.formatException(e) : 'Exception', - trace: { stack: e.stack } - }); - this.results_.addResult(expectationResult); -}; - -jasmine.Spec.prototype.getMatchersClass_ = function() { - return this.matchersClass || this.env.matchersClass; -}; - -jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { - var parent = this.getMatchersClass_(); - var newMatchersClass = function() { - parent.apply(this, arguments); - }; - jasmine.util.inherit(newMatchersClass, parent); - jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); - this.matchersClass = newMatchersClass; -}; - -jasmine.Spec.prototype.finishCallback = function() { - this.env.reporter.reportSpecResults(this); -}; - -jasmine.Spec.prototype.finish = function(onComplete) { - this.removeAllSpies(); - this.finishCallback(); - if (onComplete) { - onComplete(); - } -}; - -jasmine.Spec.prototype.after = function(doAfter) { - if (this.queue.isRunning()) { - this.queue.add(new jasmine.Block(this.env, doAfter, this), true); - } else { - this.afterCallbacks.unshift(doAfter); - } -}; - -jasmine.Spec.prototype.execute = function(onComplete) { - var spec = this; - if (!spec.env.specFilter(spec)) { - spec.results_.skipped = true; - spec.finish(onComplete); - return; - } - - this.env.reporter.reportSpecStarting(this); - - spec.env.currentSpec = spec; - - spec.addBeforesAndAftersToQueue(); - - spec.queue.start(function () { - spec.finish(onComplete); - }); -}; - -jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { - var runner = this.env.currentRunner(); - var i; - - for (var suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); - } - } - for (i = 0; i < runner.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); - } - for (i = 0; i < this.afterCallbacks.length; i++) { - this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); - } - for (suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); - } - } - for (i = 0; i < runner.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); - } -}; - -jasmine.Spec.prototype.explodes = function() { - throw 'explodes function should not have been called'; -}; - -jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { - if (obj == jasmine.undefined) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; - } - - if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { - throw methodName + '() method does not exist'; - } - - if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { - throw new Error(methodName + ' has already been spied upon'); - } - - var spyObj = jasmine.createSpy(methodName); - - this.spies_.push(spyObj); - spyObj.baseObj = obj; - spyObj.methodName = methodName; - spyObj.originalValue = obj[methodName]; - - obj[methodName] = spyObj; - - return spyObj; -}; - -jasmine.Spec.prototype.removeAllSpies = function() { - for (var i = 0; i < this.spies_.length; i++) { - var spy = this.spies_[i]; - spy.baseObj[spy.methodName] = spy.originalValue; - } - this.spies_ = []; -}; - -/** - * Internal representation of a Jasmine suite. - * - * @constructor - * @param {jasmine.Env} env - * @param {String} description - * @param {Function} specDefinitions - * @param {jasmine.Suite} parentSuite - */ -jasmine.Suite = function(env, description, specDefinitions, parentSuite) { - var self = this; - self.id = env.nextSuiteId ? env.nextSuiteId() : null; - self.description = description; - self.queue = new jasmine.Queue(env); - self.parentSuite = parentSuite; - self.env = env; - self.before_ = []; - self.after_ = []; - self.children_ = []; - self.suites_ = []; - self.specs_ = []; -}; - -jasmine.Suite.prototype.getFullName = function() { - var fullName = this.description; - for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { - fullName = parentSuite.description + ' ' + fullName; - } - return fullName; -}; - -jasmine.Suite.prototype.finish = function(onComplete) { - this.env.reporter.reportSuiteResults(this); - this.finished = true; - if (typeof(onComplete) == 'function') { - onComplete(); - } -}; - -jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.unshift(beforeEachFunction); -}; - -jasmine.Suite.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.unshift(afterEachFunction); -}; - -jasmine.Suite.prototype.results = function() { - return this.queue.results(); -}; - -jasmine.Suite.prototype.add = function(suiteOrSpec) { - this.children_.push(suiteOrSpec); - if (suiteOrSpec instanceof jasmine.Suite) { - this.suites_.push(suiteOrSpec); - this.env.currentRunner().addSuite(suiteOrSpec); - } else { - this.specs_.push(suiteOrSpec); - } - this.queue.add(suiteOrSpec); -}; - -jasmine.Suite.prototype.specs = function() { - return this.specs_; -}; - -jasmine.Suite.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Suite.prototype.children = function() { - return this.children_; -}; - -jasmine.Suite.prototype.execute = function(onComplete) { - var self = this; - this.queue.start(function () { - self.finish(onComplete); - }); -}; -jasmine.WaitsBlock = function(env, timeout, spec) { - this.timeout = timeout; - jasmine.Block.call(this, env, null, spec); -}; - -jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); - -jasmine.WaitsBlock.prototype.execute = function (onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); - } - this.env.setTimeout(function () { - onComplete(); - }, this.timeout); -}; -/** - * A block which waits for some condition to become true, with timeout. - * - * @constructor - * @extends jasmine.Block - * @param {jasmine.Env} env The Jasmine environment. - * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. - * @param {Function} latchFunction A function which returns true when the desired condition has been met. - * @param {String} message The message to display if the desired condition hasn't been met within the given time period. - * @param {jasmine.Spec} spec The Jasmine spec. - */ -jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { - this.timeout = timeout || env.defaultTimeoutInterval; - this.latchFunction = latchFunction; - this.message = message; - this.totalTimeSpentWaitingForLatch = 0; - jasmine.Block.call(this, env, null, spec); -}; -jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); - -jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; - -jasmine.WaitsForBlock.prototype.execute = function(onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); - } - var latchFunctionResult; - try { - latchFunctionResult = this.latchFunction.apply(this.spec); - } catch (e) { - this.spec.fail(e); - onComplete(); - return; - } - - if (latchFunctionResult) { - onComplete(); - } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { - var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); - this.spec.fail({ - name: 'timeout', - message: message - }); - - this.abort = true; - onComplete(); - } else { - this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; - var self = this; - this.env.setTimeout(function() { - self.execute(onComplete); - }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); - } -}; - -jasmine.version_= { - "major": 1, - "minor": 3, - "build": 1, - "revision": 1354556913 -}; diff --git a/test/browser/runner-main-spec.js b/test/browser/runner-main-spec.js index b5261e754..3308fba31 100644 --- a/test/browser/runner-main-spec.js +++ b/test/browser/runner-main-spec.js @@ -1,3 +1,4 @@ +console.warn("start spec"); describe("less.js main tests", function() { testLessEqualsInDocument(); }); diff --git a/test/browser/runner-modify-vars-spec.js b/test/browser/runner-modify-vars-spec.js index 0ce49986f..09cfe7a2e 100644 --- a/test/browser/runner-modify-vars-spec.js +++ b/test/browser/runner-modify-vars-spec.js @@ -1,43 +1,33 @@ var alreadyRun = false; -describe("less.js modify vars", function() { - beforeEach(function() { - // simulating "setUp" or "beforeAll" method - var lessOutputObj; - if (alreadyRun) - return; +describe("less.js modify vars", function () { + beforeEach(function (done) { + // simulating "setUp" or "beforeAll" method + if (alreadyRun) { + done(); + return; + } - alreadyRun = true; + alreadyRun = true; - // wait until the sheet is compiled first time - waitsFor(function() { - lessOutputObj = document.getElementById("less:test-less-simple"); - return lessOutputObj !== null; - }, "first generation of less:test-less-simple", 7000); - - // modify variables - runs(function() { - lessOutputObj.type = "not compiled yet"; - less.modifyVars({ - var1: "green", - var2: "purple", - scale: 20 - }); + less.pageLoadFinished + .then(function () { + less.modifyVars({ + var1: "green", + var2: "purple", + scale: 20 + }).then(function () { + done(); + }); + }); }); - // wait until variables are modified - waitsFor(function() { - lessOutputObj = document.getElementById("less:test-less-simple"); - return lessOutputObj !== null && lessOutputObj.type === "text/css"; - }, "second generation of less:test-less-simple", 7000); - - }); - - testLessEqualsInDocument(); - it("Should log only 2 XHR requests", function() { - var xhrLogMessages = logMessages.filter(function(item) { - return (/XHR: Getting '/).test(item); + testLessEqualsInDocument(); + it("Should log only 2 XHR requests", function (done) { + var xhrLogMessages = logMessages.filter(function (item) { + return (/XHR: Getting '/).test(item); + }); + expect(xhrLogMessages.length).toEqual(2); + done(); }); - expect(xhrLogMessages.length).toEqual(2); - }); -}); \ No newline at end of file +}); diff --git a/test/browser/test-runner-template.tmpl b/test/browser/test-runner-template.tmpl index 17f644f5f..1047c3ddc 100644 --- a/test/browser/test-runner-template.tmpl +++ b/test/browser/test-runner-template.tmpl @@ -26,7 +26,7 @@ <% }) %> - <% generateScriptTags([].concat(scripts.polyfills, scripts.jasmine)); %> + <% generateScriptTags([].concat(scripts.polyfills, scripts.jasmine, scripts.boot)); %> <% generateScriptTags(scripts.helpers); %> @@ -38,7 +38,7 @@ <% generateScriptTags(scripts.specs); %> - <% generateScriptTags([].concat(scripts.reporters, scripts.start)); %> + <% generateScriptTags([].concat(scripts.reporters)); %> From 6913f305b5e978b8a7fb03df7f983ca6ebd5e28b Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 08:34:49 +0100 Subject: [PATCH 144/161] Test why travis CI is failing --- lib/less-browser/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index f90d2602f..af78acf3e 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -14,9 +14,17 @@ var less; var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol), options = window.less || {}; +console.warn("about to apply polyfill " + window.Promise); + // shim Promise if required require('promise/polyfill.js'); +console.warn("polyfill applied" + window.Promise); + +(function() { + console.log("am I in strict mode? "+this); +})(); + window.less = less = require('../less')(); var environment = less.environment, FileManager = require("./file-manager")(options, isFileProtocol, less.logger), From 7b84a9fe54cbad01c94eab8a28201a8ffdf8a126 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 08:40:14 +0100 Subject: [PATCH 145/161] another travis ci test --- lib/less-browser/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index af78acf3e..c43e9b719 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -20,10 +20,12 @@ console.warn("about to apply polyfill " + window.Promise); require('promise/polyfill.js'); console.warn("polyfill applied" + window.Promise); - +/* global TravisCITest:true */ (function() { console.log("am I in strict mode? "+this); + TravisCITest = function(){}; })(); +console.log("TravisCITest ="+TravisCITest); window.less = less = require('../less')(); var environment = less.environment, From 545ee85a252d634d3d30222961025a6d3483ed9b Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 08:44:56 +0100 Subject: [PATCH 146/161] travis CI test 3 --- Gruntfile.js | 2 +- lib/less-browser/index.js | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 08a9dd616..5b9d9d2b5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -48,7 +48,7 @@ module.exports = function (grunt) { browser: { src: ['./lib/less-browser/index.js'], options: { - ignore: ["promise"] + //ignore: ["promise"] }, dest: 'tmp/less.js' } diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index c43e9b719..66a8fe754 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -20,12 +20,6 @@ console.warn("about to apply polyfill " + window.Promise); require('promise/polyfill.js'); console.warn("polyfill applied" + window.Promise); -/* global TravisCITest:true */ -(function() { - console.log("am I in strict mode? "+this); - TravisCITest = function(){}; -})(); -console.log("TravisCITest ="+TravisCITest); window.less = less = require('../less')(); var environment = less.environment, From f3d667bb227599e799e6816d6f3a6674f3bcbf22 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 09:43:38 +0100 Subject: [PATCH 147/161] test linux build --- Gruntfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 5b9d9d2b5..87fd1ea47 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -48,7 +48,8 @@ module.exports = function (grunt) { browser: { src: ['./lib/less-browser/index.js'], options: { - //ignore: ["promise"] + alias: ["promise/polyfill.js:promise"] + //ignore: ["promise/index.js"] }, dest: 'tmp/less.js' } From 114d3e59e80149dcbe7158845c845ec5c35d0b0d Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 09:47:39 +0100 Subject: [PATCH 148/161] Tidy up after solving problem when building in linux --- Gruntfile.js | 1 - lib/less-browser/index.js | 4 ---- 2 files changed, 5 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 87fd1ea47..c2858b213 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -49,7 +49,6 @@ module.exports = function (grunt) { src: ['./lib/less-browser/index.js'], options: { alias: ["promise/polyfill.js:promise"] - //ignore: ["promise/index.js"] }, dest: 'tmp/less.js' } diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 66a8fe754..f90d2602f 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -14,13 +14,9 @@ var less; var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol), options = window.less || {}; -console.warn("about to apply polyfill " + window.Promise); - // shim Promise if required require('promise/polyfill.js'); -console.warn("polyfill applied" + window.Promise); - window.less = less = require('../less')(); var environment = less.environment, FileManager = require("./file-manager")(options, isFileProtocol, less.logger), From e715057643837255bc6c98036b4369fc0a4ff357 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 10:00:59 +0100 Subject: [PATCH 149/161] Delete un-needed files --- test/browser/es5.js | 27 ------ test/browser/phantom-runner.js | 150 --------------------------------- 2 files changed, 177 deletions(-) delete mode 100644 test/browser/es5.js delete mode 100644 test/browser/phantom-runner.js diff --git a/test/browser/es5.js b/test/browser/es5.js deleted file mode 100644 index c4fcc8023..000000000 --- a/test/browser/es5.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - PhantomJS does not implement bind. this is from - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind - */ -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} \ No newline at end of file diff --git a/test/browser/phantom-runner.js b/test/browser/phantom-runner.js deleted file mode 100644 index 8233deb80..000000000 --- a/test/browser/phantom-runner.js +++ /dev/null @@ -1,150 +0,0 @@ -var webpage = require('webpage'); -var server = require('webserver').create(); -var system = require('system'); -var fs = require('fs'); -var host; -var port = 8081; - -var listening = server.listen(port, function(request, response) { - //console.log("Requested " + request.url); - - var filename = ("test/" + request.url.slice(1)).replace(/[\\\/]/g, fs.separator); - - if (!fs.exists(filename) || !fs.isFile(filename)) { - response.statusCode = 404; - response.write("

    File Not Found

    File:" + filename + "

    "); - response.close(); - return; - } - - // we set the headers here - response.statusCode = 200; - response.headers = { - "Cache": "no-cache", - "Content-Type": "text/html" - }; - - response.write(fs.read(filename)); - - response.close(); -}); -if (!listening) { - console.log("could not create web server listening on port " + port); - phantom.exit(); -} - -/** - * Wait until the test condition is true or a timeout occurs. Useful for waiting - * on a server response or for a ui change (fadeIn, etc.) to occur. - * - * @param testFx javascript condition that evaluates to a boolean, - * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or - * as a callback function. - * @param onReady what to do when testFx condition is fulfilled, - * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or - * as a callback function. - * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. - * @param timeOutErrorMessage the error message if time out occurs - */ - -function waitFor(testFx, onReady, timeOutMillis, timeOutErrorMessage) { - var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10001, //< Default Max Timeout is 10s - start = new Date().getTime(), - condition = false, - interval = setInterval(function() { - if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { - // If not time-out yet and condition not yet fulfilled - condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code - } else { - if (!condition) { - // If condition still not fulfilled (timeout but condition is 'false') - console.log(timeOutErrorMessage || "'waitFor()' timeout"); - phantom.exit(1); - } else { - // Condition fulfilled (timeout and/or condition is 'true') - typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled - clearInterval(interval); //< Stop this interval - } - } - }, 100); //< repeat check every 100ms -} - -function testPage(url) { - var page = webpage.create(); - page.open(url, function(status) { - if (status !== "success") { - console.log("Unable to access network - " + status); - phantom.exit(); - } else { - waitFor(function() { - return page.evaluate(function() { - return document.body && document.body.querySelector && - document.body.querySelector('.symbolSummary .pending') === null && - document.body.querySelector('.results') !== null; - }); - }, function() { - page.onConsoleMessage = function(msg) { - console.log(msg); - }; - var exitCode = page.evaluate(function() { - console.log(''); - console.log(document.body.querySelector('.description').innerText); - var list = document.body.querySelectorAll('.results > #details > .specDetail.failed'); - if (list && list.length > 0) { - console.log(''); - console.log(list.length + ' test(s) FAILED:'); - for (var i = 0; i < list.length; ++i) { - var el = list[i], - desc = el.querySelector('.description'), - msg = el.querySelector('.resultMessage.fail'); - console.log(''); - console.log(desc.innerText); - console.log(msg.innerText); - console.log(''); - } - return 1; - } else { - console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText); - return 0; - } - }); - testFinished(exitCode); - }, - 10000, // 10 second timeout - "Test failed waiting for jasmine results on page: " + url); - } - }); -} - -function scanDirectory(path, regex) { - var files = []; - fs.list(path).forEach(function(file) { - if (file.match(regex)) { - files.push(file); - } - }); - return files; -} - -var totalTests = 0, - totalFailed = 0, - totalDone = 0; - -function testFinished(failed) { - if (failed) { - totalFailed++; - } - totalDone++; - if (totalDone === totalTests) { - phantom.exit(totalFailed > 0 ? 1 : 0); - } -} - -if (system.args.length != 2 && system.args[1] != "--no-tests") { - var files = scanDirectory("test/browser/", /^test-runner-.+\.htm$/); - totalTests = files.length; - console.log("found " + files.length + " tests"); - files.forEach(function(file) { - testPage("http://localhost:8081/browser/" + file); - }); -} \ No newline at end of file From b8736e0b6700c915ae3f3210c57a6e225dd740f5 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 10:01:18 +0100 Subject: [PATCH 150/161] whitespace --- test/browser/runner-no-js-errors-spec.js | 2 +- test/browser/runner-postProcessor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/browser/runner-no-js-errors-spec.js b/test/browser/runner-no-js-errors-spec.js index 38aefe292..6c0e0840b 100644 --- a/test/browser/runner-no-js-errors-spec.js +++ b/test/browser/runner-no-js-errors-spec.js @@ -1,4 +1,4 @@ describe("less.js javascript disabled error tests", function() { - testLessErrorsInDocument(); + testLessErrorsInDocument(); }); diff --git a/test/browser/runner-postProcessor.js b/test/browser/runner-postProcessor.js index be9eb24bb..a104a59b1 100644 --- a/test/browser/runner-postProcessor.js +++ b/test/browser/runner-postProcessor.js @@ -1,3 +1,3 @@ describe("less.js postProcessor", function() { - testLessEqualsInDocument(); + testLessEqualsInDocument(); }); From 845ec72131c0d28e3c6447e2ac5f40bc5fe35cf1 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 16:48:04 +0100 Subject: [PATCH 151/161] improve error reporting --- lib/less-browser/browser.js | 2 +- lib/less-browser/error-reporting.js | 10 ++++++---- lib/less-browser/index.js | 1 - lib/less-browser/log-listener.js | 4 ++-- lib/less/functions/color.js | 2 +- lib/less/parser/parser.js | 2 +- lib/less/tree/expression.js | 11 +++++++---- lib/less/tree/value.js | 3 +++ test/browser/common.js | 6 ++++-- 9 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/less-browser/browser.js b/lib/less-browser/browser.js index c289a1551..fdb1d06e3 100644 --- a/lib/less-browser/browser.js +++ b/lib/less-browser/browser.js @@ -50,7 +50,7 @@ module.exports = { try { css.styleSheet.cssText = styles; } catch (e) { - throw new(Error)("Couldn't reassign styleSheet.cssText."); + throw new Error("Couldn't reassign styleSheet.cssText."); } } } diff --git a/lib/less-browser/error-reporting.js b/lib/less-browser/error-reporting.js index 54d23c26f..a358d13db 100644 --- a/lib/less-browser/error-reporting.js +++ b/lib/less-browser/error-reporting.js @@ -30,8 +30,9 @@ module.exports = function(window, less, options) { errorline(e, 2, ''); content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + '
      ' + errors.join('') + '
    '; - } else if (e.stack) { - content += '
    ' + e.stack.split('\n').slice(1).join('
    '); + } + if (e.stack && (e.extract || options.logLevel >= 4)) { + content += '
    Stack Trace
    ' + e.stack.split('\n').slice(1).join('
    '); } elem.innerHTML = content; @@ -155,8 +156,9 @@ module.exports = function(window, less, options) { errorline(e, 2, ''); content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + errors.join('\n'); - } else if (e.stack) { - content += e.stack; + } + if (e.stack && (e.extract || options.logLevel >= 4)) { + content += '\nStack Trace\n' + e.stack; } less.logger.error(content); } diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index f90d2602f..6cb35492e 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -25,7 +25,6 @@ environment.addFileManager(fileManager); less.FileManager = FileManager; require("./log-listener")(less, options); -//var utils = require("./utils"); var errors = require("./error-reporting")(window, less, options); var browser = require("./browser"); var cache = options.cache || require("./cache")(window, options, less.logger); diff --git a/lib/less-browser/log-listener.js b/lib/less-browser/log-listener.js index 9806c9965..199a499d0 100644 --- a/lib/less-browser/log-listener.js +++ b/lib/less-browser/log-listener.js @@ -8,10 +8,10 @@ module.exports = function(less, options) { // The amount of logging in the javascript console. // 3 - Debug, information and errors // 2 - Information and errors - // 1 - Errors + // 1 - Errors // 0 - None // Defaults to 2 - options.logLevel = typeof(options.logLevel) !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_debug : logLevel_error); + options.logLevel = typeof(options.logLevel) !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_info : logLevel_error); if (!options.loggers) { options.loggers = [{ diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js index ebdd042a5..5355823d3 100644 --- a/lib/less/functions/color.js +++ b/lib/less/functions/color.js @@ -18,7 +18,7 @@ function number(n) { return n; } else { throw { - error: "RuntimeError", + type: "Argument", message: "color functions take numbers as parameters" }; } diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js index b04392ba1..099d34689 100644 --- a/lib/less/parser/parser.js +++ b/lib/less/parser/parser.js @@ -113,7 +113,7 @@ var Parser = function Parser(context, imports, fileInfo) { // output. The callback is called when the input is parsed. try { parserInput.start(str, context.chunkInput, function fail(msg, index) { - throw new(LessError)({ + throw LessError({ index: index, type: 'Parse', message: msg, diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 59e8a40e1..3083e3b89 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -2,13 +2,16 @@ var Node = require("./node"), Paren = require("./paren"), Comment = require("./comment"); -var Expression = function (value) { this.value = value; }; +var Expression = function (value) { + this.value = value; + if (!value) { + throw new Error("Expression requires a array parameter"); + } +}; Expression.prototype = new Node(); Expression.prototype.type = "Expression"; Expression.prototype.accept = function (visitor) { - if (this.value) { - this.value = visitor.visitArray(this.value); - } + this.value = visitor.visitArray(this.value); }; Expression.prototype.eval = function (context) { var returnValue, diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index ebdff007f..ee31c834a 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -2,6 +2,9 @@ var Node = require("./node"); var Value = function (value) { this.value = value; + if (!value) { + throw new Error("Value requires an array argument"); + } }; Value.prototype = new Node(); Value.prototype.type = "Value"; diff --git a/test/browser/common.js b/test/browser/common.js index 1ca4e7a46..4e9ea54a1 100644 --- a/test/browser/common.js +++ b/test/browser/common.js @@ -103,7 +103,8 @@ var testErrorSheet = function (sheet) { return lineNo + " "; }) .replace(/\n\s*in /g, " in ") - .replace("\n\n", "\n"); + .replace("\n\n", "\n") + .replace(/\nStack Trace\n[\s\S]*/, ""); errorFile .then(function (errorTxt) { errorTxt = errorTxt @@ -128,7 +129,8 @@ var testErrorSheetConsole = function (sheet) { errorHref = lessHref.replace(/.less$/, ".txt"), errorFile = loadFile(errorHref), actualErrorElement = document.getElementById(id), - actualErrorMsg = logMessages[logMessages.length - 1]; + actualErrorMsg = logMessages[logMessages.length - 1] + .replace(/\nStack Trace\n[\s\S]*/, ""); describe("the error", function () { expect(actualErrorElement).toBe(null); From 9d535c0470eb852b8d69ba2f3b1e10d55f946a4f Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 16:57:22 +0100 Subject: [PATCH 152/161] Whitespace - undo unusual new() syntax --- benchmark/less-benchmark.js | 4 ++-- lib/less-browser/cache.js | 4 ++-- lib/less/functions/color-blending.js | 2 +- lib/less/functions/color.js | 36 ++++++++++++++-------------- lib/less/functions/math.js | 2 +- lib/less/functions/number.js | 18 +++++++------- lib/less/functions/string.js | 8 +++---- lib/less/functions/types.js | 4 ++-- lib/less/transform-tree.js | 24 +++++++++---------- lib/less/tree/assignment.js | 2 +- lib/less/tree/attribute.js | 2 +- lib/less/tree/color.js | 6 ++--- lib/less/tree/dimension.js | 8 +++---- lib/less/tree/directive.js | 2 +- lib/less/tree/element.js | 4 ++-- lib/less/tree/expression.js | 4 ++-- lib/less/tree/extend.js | 4 ++-- lib/less/tree/import.js | 12 +++++----- lib/less/tree/javascript.js | 8 +++---- lib/less/tree/js-eval-node.js | 4 ++-- lib/less/tree/keyword.js | 4 ++-- lib/less/tree/media.js | 24 +++++++++---------- lib/less/tree/mixin-call.js | 2 +- lib/less/tree/mixin-definition.js | 14 +++++------ lib/less/tree/negative.js | 4 ++-- lib/less/tree/operation.js | 2 +- lib/less/tree/paren.js | 2 +- lib/less/tree/quoted.js | 4 ++-- lib/less/tree/rule.js | 6 ++--- lib/less/tree/ruleset-call.js | 2 +- lib/less/tree/ruleset.js | 12 +++++----- lib/less/tree/selector.js | 2 +- lib/less/tree/value.js | 2 +- lib/less/tree/variable.js | 2 +- lib/less/visitors/import-visitor.js | 2 +- 35 files changed, 121 insertions(+), 121 deletions(-) diff --git a/benchmark/less-benchmark.js b/benchmark/less-benchmark.js index de6762385..58a0d71a2 100644 --- a/benchmark/less-benchmark.js +++ b/benchmark/less-benchmark.js @@ -12,9 +12,9 @@ fs.readFile(file, 'utf8', function (e, data) { console.log("Benchmarking...\n", path.basename(file) + " (" + parseInt(data.length / 1024) + " KB)", ""); - start = new(Date); + start = new Date(); - new(less.Parser)().parse(data, function (err, tree) { + new less.Parser().parse(data, function (err, tree) { end = new Date(); total = end - start; diff --git a/lib/less-browser/cache.js b/lib/less-browser/cache.js index fd7a45497..3a4a70951 100644 --- a/lib/less-browser/cache.js +++ b/lib/less-browser/cache.js @@ -25,8 +25,8 @@ module.exports = function(window, options, logger) { timestamp = cache && cache.getItem(path + ':timestamp'); if (timestamp && webInfo.lastModified && - (new(Date)(webInfo.lastModified).valueOf() === - new(Date)(timestamp).valueOf())) { + (new Date(webInfo.lastModified).valueOf() === + new Date(timestamp).valueOf())) { // Use local copy return css; } diff --git a/lib/less/functions/color-blending.js b/lib/less/functions/color-blending.js index 537045108..4d62c9a96 100644 --- a/lib/less/functions/color-blending.js +++ b/lib/less/functions/color-blending.js @@ -21,7 +21,7 @@ function colorBlend(mode, color1, color2) { r[i] = cr * 255; } - return new(Color)(r, ar); + return new Color(r, ar); } var colorBlendModeFunctions = { diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js index 5355823d3..515813289 100644 --- a/lib/less/functions/color.js +++ b/lib/less/functions/color.js @@ -37,7 +37,7 @@ colorFunctions = { rgba: function (r, g, b, a) { var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); a = number(a); - return new(Color)(rgb, a); + return new Color(rgb, a); }, hsl: function (h, s, l) { return colorFunctions.hsla(h, s, l, 1.0); @@ -93,37 +93,37 @@ colorFunctions = { }, hue: function (color) { - return new(Dimension)(color.toHSL().h); + return new Dimension(color.toHSL().h); }, saturation: function (color) { - return new(Dimension)(color.toHSL().s * 100, '%'); + return new Dimension(color.toHSL().s * 100, '%'); }, lightness: function (color) { - return new(Dimension)(color.toHSL().l * 100, '%'); + return new Dimension(color.toHSL().l * 100, '%'); }, hsvhue: function(color) { - return new(Dimension)(color.toHSV().h); + return new Dimension(color.toHSV().h); }, hsvsaturation: function (color) { - return new(Dimension)(color.toHSV().s * 100, '%'); + return new Dimension(color.toHSV().s * 100, '%'); }, hsvvalue: function (color) { - return new(Dimension)(color.toHSV().v * 100, '%'); + return new Dimension(color.toHSV().v * 100, '%'); }, red: function (color) { - return new(Dimension)(color.rgb[0]); + return new Dimension(color.rgb[0]); }, green: function (color) { - return new(Dimension)(color.rgb[1]); + return new Dimension(color.rgb[1]); }, blue: function (color) { - return new(Dimension)(color.rgb[2]); + return new Dimension(color.rgb[2]); }, alpha: function (color) { - return new(Dimension)(color.toHSL().a); + return new Dimension(color.toHSL().a); }, luma: function (color) { - return new(Dimension)(color.luma() * color.alpha * 100, '%'); + return new Dimension(color.luma() * color.alpha * 100, '%'); }, luminance: function (color) { var luminance = @@ -131,7 +131,7 @@ colorFunctions = { + (0.7152 * color.rgb[1] / 255) + (0.0722 * color.rgb[2] / 255); - return new(Dimension)(luminance * color.alpha * 100, '%'); + return new Dimension(luminance * color.alpha * 100, '%'); }, saturate: function (color, amount) { // filter: saturate(3.2); @@ -201,7 +201,7 @@ colorFunctions = { // mix: function (color1, color2, weight) { if (!weight) { - weight = new(Dimension)(50); + weight = new Dimension(50); } var p = weight.value / 100.0; var w = p * 2 - 1; @@ -216,10 +216,10 @@ colorFunctions = { var alpha = color1.alpha * p + color2.alpha * (1 - p); - return new(Color)(rgb, alpha); + return new Color(rgb, alpha); }, greyscale: function (color) { - return colorFunctions.desaturate(color, new(Dimension)(100)); + return colorFunctions.desaturate(color, new Dimension(100)); }, contrast: function (color, dark, light, threshold) { // filter: contrast(3.2); @@ -251,12 +251,12 @@ colorFunctions = { } }, argb: function (color) { - return new(Anonymous)(color.toARGB()); + return new Anonymous(color.toARGB()); }, color: function(c) { if ((c instanceof Quoted) && (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { - return new(Color)(c.value.slice(1)); + return new Color(c.value.slice(1)); } if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) { c.keyword = undefined; diff --git a/lib/less/functions/math.js b/lib/less/functions/math.js index b345fc392..206751959 100644 --- a/lib/less/functions/math.js +++ b/lib/less/functions/math.js @@ -24,7 +24,7 @@ function _math(fn, unit, n) { } else { n = n.unify(); } - return new(Dimension)(fn(parseFloat(n.value)), unit); + return new Dimension(fn(parseFloat(n.value)), unit); } for (var f in mathFunctions) { diff --git a/lib/less/functions/number.js b/lib/less/functions/number.js index c4fb3ae22..464323f51 100644 --- a/lib/less/functions/number.js +++ b/lib/less/functions/number.js @@ -19,7 +19,7 @@ var minMax = function (isMin, args) { } continue; } - currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(Dimension)(current.value, unitClone).unify() : current.unify(); + currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify(); unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; @@ -32,7 +32,7 @@ var minMax = function (isMin, args) { order.push(current); continue; } - referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(Dimension)(order[j].value, unitClone).unify() : order[j].unify(); + referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify(); if ( isMin && currentUnified.value < referenceUnified.value || !isMin && currentUnified.value > referenceUnified.value) { order[j] = current; @@ -42,7 +42,7 @@ var minMax = function (isMin, args) { return order[0]; } args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? "," : ", "); - return new(Anonymous)((isMin ? "min" : "max") + "(" + args + ")"); + return new Anonymous((isMin ? "min" : "max") + "(" + args + ")"); }; functionRegistry.addMultiple({ min: function () { @@ -55,22 +55,22 @@ functionRegistry.addMultiple({ return val.convertTo(unit.value); }, pi: function () { - return new(Dimension)(Math.PI); + return new Dimension(Math.PI); }, mod: function(a, b) { - return new(Dimension)(a.value % b.value, a.unit); + return new Dimension(a.value % b.value, a.unit); }, pow: function(x, y) { if (typeof x === "number" && typeof y === "number") { - x = new(Dimension)(x); - y = new(Dimension)(y); + x = new Dimension(x); + y = new Dimension(y); } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) { throw { type: "Argument", message: "arguments must be numbers" }; } - return new(Dimension)(Math.pow(x.value, y.value), x.unit); + return new Dimension(Math.pow(x.value, y.value), x.unit); }, percentage: function (n) { - return new(Dimension)(n.value * 100, '%'); + return new Dimension(n.value * 100, '%'); } }); diff --git a/lib/less/functions/string.js b/lib/less/functions/string.js index efeb81005..1b55d4cf3 100644 --- a/lib/less/functions/string.js +++ b/lib/less/functions/string.js @@ -5,16 +5,16 @@ var Quoted = require("../tree/quoted"), functionRegistry.addMultiple({ e: function (str) { - return new(Anonymous)(str instanceof JavaScript ? str.evaluated : str.value); + return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value); }, escape: function (str) { - return new(Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); + return new Anonymous(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); }, replace: function (string, pattern, replacement, flags) { var result = string.value; result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); - return new(Quoted)(string.quote || '', result, string.escaped); + return new Quoted(string.quote || '', result, string.escaped); }, '%': function (string /* arg, arg, ...*/) { var args = Array.prototype.slice.call(arguments, 1), @@ -28,6 +28,6 @@ functionRegistry.addMultiple({ }); } result = result.replace(/%%/g, '%'); - return new(Quoted)(string.quote || '', result, string.escaped); + return new Quoted(string.quote || '', result, string.escaped); } }); diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js index 2870f0331..d1a58be53 100644 --- a/lib/less/functions/types.js +++ b/lib/less/functions/types.js @@ -52,10 +52,10 @@ functionRegistry.addMultiple({ } else { unit = ""; } - return new(Dimension)(val.value, unit); + return new Dimension(val.value, unit); }, "get-unit": function (n) { - return new(Anonymous)(n.unit); + return new Anonymous(n.unit); }, extract: function(values, index) { index = index.value - 1; // (1-based index) diff --git a/lib/less/transform-tree.js b/lib/less/transform-tree.js index 5a456de36..5b2f1db45 100644 --- a/lib/less/transform-tree.js +++ b/lib/less/transform-tree.js @@ -11,12 +11,12 @@ module.exports = function(root, options) { // // Allows setting variables with a hash, so: // - // `{ color: new(tree.Color)('#f01') }` will become: + // `{ color: new tree.Color('#f01') }` will become: // - // new(tree.Rule)('@color', - // new(tree.Value)([ - // new(tree.Expression)([ - // new(tree.Color)('#f01') + // new tree.Rule('@color', + // new tree.Value([ + // new tree.Expression([ + // new tree.Color('#f01') // ]) // ]) // ) @@ -27,20 +27,20 @@ module.exports = function(root, options) { if (! (value instanceof tree.Value)) { if (! (value instanceof tree.Expression)) { - value = new(tree.Expression)([value]); + value = new tree.Expression([value]); } - value = new(tree.Value)([value]); + value = new tree.Value([value]); } - return new(tree.Rule)('@' + k, value, false, null, 0); + return new tree.Rule('@' + k, value, false, null, 0); }); - evalEnv.frames = [new(tree.Ruleset)(null, variables)]; + evalEnv.frames = [new tree.Ruleset(null, variables)]; } var preEvalVisitors = [], visitors = [ - new(visitor.JoinSelectorVisitor)(), - new(visitor.ExtendVisitor)(), - new(visitor.ToCSSVisitor)({compress: Boolean(options.compress)}) + new visitor.JoinSelectorVisitor(), + new visitor.ExtendVisitor(), + new visitor.ToCSSVisitor({compress: Boolean(options.compress)}) ], i; if (options.pluginManager) { diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index d647b4eca..ec91263e3 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -12,7 +12,7 @@ Assignment.prototype.accept = function (visitor) { }; Assignment.prototype.eval = function (context) { if (this.value.eval) { - return new(Assignment)(this.key, this.value.eval(context)); + return new Assignment(this.key, this.value.eval(context)); } return this; }; diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js index 359631542..488ded795 100644 --- a/lib/less/tree/attribute.js +++ b/lib/less/tree/attribute.js @@ -8,7 +8,7 @@ var Attribute = function (key, op, value) { Attribute.prototype = new Node(); Attribute.prototype.type = "Attribute"; Attribute.prototype.eval = function (context) { - return new(Attribute)(this.key.eval ? this.key.eval(context) : this.key, + return new Attribute(this.key.eval ? this.key.eval(context) : this.key, this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value); }; Attribute.prototype.genCSS = function (context, output) { diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index de903c582..cfaccf367 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -101,7 +101,7 @@ Color.prototype.operate = function (context, op, other) { for (var c = 0; c < 3; c++) { rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]); } - return new(Color)(rgb, alpha); + return new Color(rgb, alpha); }; Color.prototype.toRGB = function () { return toHex(this.rgb); @@ -172,10 +172,10 @@ Color.prototype.compare = function (x) { Color.fromKeyword = function(keyword) { var c, key = keyword.toLowerCase(); if (colors.hasOwnProperty(key)) { - c = new(Color)(colors[key].slice(1)); + c = new Color(colors[key].slice(1)); } else if (key === "transparent") { - c = new(Color)([0, 0, 0], 0); + c = new Color([0, 0, 0], 0); } if (c) { diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index c5bf45369..1f8de7bb2 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -9,7 +9,7 @@ var Node = require("./node"), var Dimension = function (value, unit) { this.value = parseFloat(value); this.unit = (unit && unit instanceof Unit) ? unit : - new(Unit)(unit ? [unit] : undefined); + new Unit(unit ? [unit] : undefined); }; Dimension.prototype = new Node(); @@ -21,7 +21,7 @@ Dimension.prototype.eval = function (context) { return this; }; Dimension.prototype.toColor = function () { - return new(Color)([this.value, this.value, this.value]); + return new Color([this.value, this.value, this.value]); }; Dimension.prototype.genCSS = function (context, output) { if ((context && context.strictUnits) && !this.unit.isSingular()) { @@ -86,7 +86,7 @@ Dimension.prototype.operate = function (context, op, other) { unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); unit.cancel(); } - return new(Dimension)(value, unit); + return new Dimension(value, unit); }; Dimension.prototype.compare = function (other) { var a, b; @@ -150,6 +150,6 @@ Dimension.prototype.convertTo = function (conversions) { unit.cancel(); - return new(Dimension)(value, unit); + return new Dimension(value, unit); }; module.exports = Dimension; diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index 37c5cb1e7..3645cb044 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -52,7 +52,7 @@ Directive.prototype.eval = function (context) { rules = rules.eval(context); rules.root = true; } - return new(Directive)(this.name, value, rules, + return new Directive(this.name, value, rules, this.index, this.currentFileInfo, this.debugInfo); }; Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); }; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index df46b00ca..228528116 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -4,7 +4,7 @@ var Node = require("./node"), var Element = function (combinator, value, index, currentFileInfo) { this.combinator = combinator instanceof Combinator ? - combinator : new(Combinator)(combinator); + combinator : new Combinator(combinator); if (typeof(value) === 'string') { this.value = value.trim(); @@ -26,7 +26,7 @@ Element.prototype.accept = function (visitor) { } }; Element.prototype.eval = function (context) { - return new(Element)(this.combinator, + return new Element(this.combinator, this.value.eval ? this.value.eval(context) : this.value, this.index, this.currentFileInfo); diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 3083e3b89..00024ceb5 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -21,7 +21,7 @@ Expression.prototype.eval = function (context) { context.inParenthesis(); } if (this.value.length > 1) { - returnValue = new(Expression)(this.value.map(function (e) { + returnValue = new Expression(this.value.map(function (e) { return e.eval(context); })); } else if (this.value.length === 1) { @@ -36,7 +36,7 @@ Expression.prototype.eval = function (context) { context.outOfParenthesis(); } if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) { - returnValue = new(Paren)(returnValue); + returnValue = new Paren(returnValue); } return returnValue; }; diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js index 7758ea1f1..f95d0af61 100644 --- a/lib/less/tree/extend.js +++ b/lib/less/tree/extend.js @@ -26,10 +26,10 @@ Extend.prototype.accept = function (visitor) { this.selector = visitor.visit(this.selector); }; Extend.prototype.eval = function (context) { - return new(Extend)(this.selector.eval(context), this.option, this.index); + return new Extend(this.selector.eval(context), this.option, this.index); }; Extend.prototype.clone = function (context) { - return new(Extend)(this.selector, this.option, this.index); + return new Extend(this.selector, this.option, this.index); }; Extend.prototype.findSelfSelectors = function (selectors) { var selfElements = [], diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index f1e5cae26..5183d761e 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -79,7 +79,7 @@ Import.prototype.evalForImport = function (context) { if (path instanceof URL) { path = path.value; } - return new(Import)(path.eval(context), this.features, this.options, this.index, this.currentFileInfo); + return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo); }; Import.prototype.evalPath = function (context) { var path = this.path.eval(context); @@ -111,20 +111,20 @@ Import.prototype.eval = function (context) { } if (this.options.inline) { - var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true); - return this.features ? new(Media)([contents], this.features.value) : [contents]; + var contents = new Anonymous(this.root, 0, {filename: this.importedFilename}, true, true); + return this.features ? new Media([contents], this.features.value) : [contents]; } else if (this.css) { - var newImport = new(Import)(this.evalPath(context), features, this.options, this.index); + var newImport = new Import(this.evalPath(context), features, this.options, this.index); if (!newImport.css && this.error) { throw this.error; } return newImport; } else { - ruleset = new(Ruleset)(null, this.root.rules.slice(0)); + ruleset = new Ruleset(null, this.root.rules.slice(0)); ruleset.evalImports(context); - return this.features ? new(Media)(ruleset.rules, this.features.value) : ruleset.rules; + return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules; } }; module.exports = Import; diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index b951bfa96..7f17787ab 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -15,13 +15,13 @@ JavaScript.prototype.eval = function(context) { var result = this.evaluateJavaScript(this.expression, context); if (typeof(result) === 'number') { - return new(Dimension)(result); + return new Dimension(result); } else if (typeof(result) === 'string') { - return new(Quoted)('"' + result + '"', result, this.escaped, this.index); + return new Quoted('"' + result + '"', result, this.escaped, this.index); } else if (Array.isArray(result)) { - return new(Anonymous)(result.join(', ')); + return new Anonymous(result.join(', ')); } else { - return new(Anonymous)(result); + return new Anonymous(result); } }; diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js index c9f86c733..30fbbfae1 100644 --- a/lib/less/tree/js-eval-node.js +++ b/lib/less/tree/js-eval-node.js @@ -17,11 +17,11 @@ JsEvalNode.prototype.evaluateJavaScript = function (expression, context) { } expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { - return that.jsify(new(Variable)('@' + name, that.index, that.currentFileInfo).eval(context)); + return that.jsify(new Variable('@' + name, that.index, that.currentFileInfo).eval(context)); }); try { - expression = new(Function)('return (' + expression + ')'); + expression = new Function('return (' + expression + ')'); } catch (e) { throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , filename: this.currentFileInfo.filename, diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index cbf3db4ff..cbb508e06 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -8,7 +8,7 @@ Keyword.prototype.genCSS = function (context, output) { output.add(this.value); }; -Keyword.True = new(Keyword)('true'); -Keyword.False = new(Keyword)('false'); +Keyword.True = new Keyword('true'); +Keyword.False = new Keyword('false'); module.exports = Keyword; diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 8ec992770..e1436c131 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -12,8 +12,8 @@ var Media = function (value, features, index, currentFileInfo) { var selectors = this.emptySelectors(); - this.features = new(Value)(features); - this.rules = [new(Ruleset)(selectors, value)]; + this.features = new Value(features); + this.rules = [new Ruleset(selectors, value)]; this.rules[0].allowImports = true; }; Media.prototype = new Directive(); @@ -38,7 +38,7 @@ Media.prototype.eval = function (context) { context.mediaPath = []; } - var media = new(Media)(null, [], this.index, this.currentFileInfo); + var media = new Media(null, [], this.index, this.currentFileInfo); if(this.debugInfo) { this.rules[0].debugInfo = this.debugInfo; media.debugInfo = this.debugInfo; @@ -74,8 +74,8 @@ Media.prototype.variable = function (name) { return Ruleset.prototype.variable.c Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); }; Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); }; Media.prototype.emptySelectors = function() { - var el = new(Element)('', '&', this.index, this.currentFileInfo), - sels = [new(Selector)([el], null, null, this.index, this.currentFileInfo)]; + var el = new Element('', '&', this.index, this.currentFileInfo), + sels = [new Selector([el], null, null, this.index, this.currentFileInfo)]; sels[0].mediaEmpty = true; return sels; }; @@ -95,7 +95,7 @@ Media.prototype.evalTop = function (context) { // Render all dependent Media blocks. if (context.mediaBlocks.length > 1) { var selectors = this.emptySelectors(); - result = new(Ruleset)(selectors, context.mediaBlocks); + result = new Ruleset(selectors, context.mediaBlocks); result.multiMedia = true; } @@ -122,20 +122,20 @@ Media.prototype.evalNested = function (context) { // a and e // b and c and d // b and c and e - this.features = new(Value)(this.permute(path).map(function (path) { + this.features = new Value(this.permute(path).map(function (path) { path = path.map(function (fragment) { - return fragment.toCSS ? fragment : new(Anonymous)(fragment); + return fragment.toCSS ? fragment : new Anonymous(fragment); }); for(i = path.length - 1; i > 0; i--) { - path.splice(i, 0, new(Anonymous)("and")); + path.splice(i, 0, new Anonymous("and")); } - return new(Expression)(path); + return new Expression(path); })); // Fake a tree-node that doesn't output anything. - return new(Ruleset)([], []); + return new Ruleset([], []); }; Media.prototype.permute = function (arr) { if (arr.length === 0) { @@ -156,6 +156,6 @@ Media.prototype.permute = function (arr) { Media.prototype.bubbleSelectors = function (selectors) { if (!selectors) return; - this.rules = [new(Ruleset)(selectors.slice(0), [this.rules[0]])]; + this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])]; }; module.exports = Media; diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js index b5e93b4e0..c4cf32d9a 100644 --- a/lib/less/tree/mixin-call.js +++ b/lib/less/tree/mixin-call.js @@ -4,7 +4,7 @@ var Node = require("./node"), defaultFunc = require("../functions/default"); var MixinCall = function (elements, args, index, currentFileInfo, important) { - this.selector = new(Selector)(elements); + this.selector = new Selector(elements); this.arguments = (args && args.length) ? args : null; this.index = index; this.currentFileInfo = currentFileInfo; diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js index 26dc699bc..41b74a184 100644 --- a/lib/less/tree/mixin-definition.js +++ b/lib/less/tree/mixin-definition.js @@ -7,7 +7,7 @@ var Selector = require("./selector"), var Definition = function (name, params, rules, condition, variadic, frames) { this.name = name; - this.selectors = [new(Selector)([new(Element)(null, name, this.index, this.currentFileInfo)])]; + this.selectors = [new Selector([new Element(null, name, this.index, this.currentFileInfo)])]; this.params = params; this.condition = condition; this.variadic = variadic; @@ -34,7 +34,7 @@ Definition.prototype.accept = function (visitor) { }; Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) { /*jshint boss:true */ - var frame = new(Ruleset)(null, null), + var frame = new Ruleset(null, null), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex, argsLength = 0; @@ -52,7 +52,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume for(j = 0; j < params.length; j++) { if (!evaldArguments[j] && name === params[j].name) { evaldArguments[j] = arg.value.eval(context); - frame.prependRule(new(Rule)(name, arg.value.eval(context))); + frame.prependRule(new Rule(name, arg.value.eval(context))); isNamedFound = true; break; } @@ -80,7 +80,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume for (j = argIndex; j < argsLength; j++) { varargs.push(args[j].value.eval(context)); } - frame.prependRule(new(Rule)(name, new(Expression)(varargs).eval(context))); + frame.prependRule(new Rule(name, new Expression(varargs).eval(context))); } else { val = arg && arg.value; if (val) { @@ -93,7 +93,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume ' (' + argsLength + ' for ' + this.arity + ')' }; } - frame.prependRule(new(Rule)(name, val)); + frame.prependRule(new Rule(name, val)); evaldArguments[i] = val; } } @@ -117,11 +117,11 @@ Definition.prototype.evalCall = function (context, args, important) { frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments), rules, ruleset; - frame.prependRule(new(Rule)('@arguments', new Expression(_arguments).eval(context))); + frame.prependRule(new Rule('@arguments', new Expression(_arguments).eval(context))); rules = this.rules.slice(0); - ruleset = new(Ruleset)(null, rules); + ruleset = new Ruleset(null, rules); ruleset.originalRuleset = this; ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames))); if (important) { diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index 30b1a98ea..228e8a66c 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -13,8 +13,8 @@ Negative.prototype.genCSS = function (context, output) { }; Negative.prototype.eval = function (context) { if (context.isMathOn()) { - return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(context); + return (new Operation('*', [new Dimension(-1), this.value])).eval(context); } - return new(Negative)(this.value.eval(context)); + return new Negative(this.value.eval(context)); }; module.exports = Negative; diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index a95021a82..ec40d3ce0 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -30,7 +30,7 @@ Operation.prototype.eval = function (context) { return a.operate(context, this.op, b); } else { - return new(Operation)(this.op, [a, b], this.isSpaced); + return new Operation(this.op, [a, b], this.isSpaced); } }; Operation.prototype.genCSS = function (context, output) { diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index a6f77921b..a02ae325f 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -11,6 +11,6 @@ Paren.prototype.genCSS = function (context, output) { output.add(')'); }; Paren.prototype.eval = function (context) { - return new(Paren)(this.value.eval(context)); + return new Paren(this.value.eval(context)); }; module.exports = Paren; diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 6974ab6a9..c7d830511 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -26,7 +26,7 @@ Quoted.prototype.eval = function (context) { return String(that.evaluateJavaScript(exp, context)); }; var interpolationReplacement = function (_, name) { - var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(context, true); + var v = new Variable('@' + name, that.index, that.currentFileInfo).eval(context, true); return (v instanceof Quoted) ? v.value : v.toCSS(); }; function iterativeReplace(value, regexp, replacementFnc) { @@ -39,7 +39,7 @@ Quoted.prototype.eval = function (context) { } value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement); value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement); - return new(Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); + return new Quoted(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); }; Quoted.prototype.compare = function (other) { // when comparing quoted strings allow the quote to differ diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index f395c6a64..402ebc509 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -4,7 +4,7 @@ var Node = require("./node"), var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) { this.name = name; - this.value = (value instanceof Node) ? value : new(Value)([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ?? + this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ?? this.important = important ? ' ' + important.trim() : ''; this.merge = merge; this.index = index; @@ -59,7 +59,7 @@ Rule.prototype.eval = function (context) { index: this.index, filename: this.currentFileInfo.filename }; } - return new(Rule)(name, + return new Rule(name, evaldValue, this.important, this.merge, @@ -80,7 +80,7 @@ Rule.prototype.eval = function (context) { } }; Rule.prototype.makeImportant = function () { - return new(Rule)(this.name, + return new Rule(this.name, this.value, "!important", this.merge, diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js index 3cb91c982..7a62c14dd 100644 --- a/lib/less/tree/ruleset-call.js +++ b/lib/less/tree/ruleset-call.js @@ -7,7 +7,7 @@ var RulesetCall = function (variable) { RulesetCall.prototype = new Node(); RulesetCall.prototype.type = "RulesetCall"; RulesetCall.prototype.eval = function (context) { - var detachedRuleset = new(Variable)(this.variable).eval(context); + var detachedRuleset = new Variable(this.variable).eval(context); return detachedRuleset.callEval(context); }; module.exports = RulesetCall; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 23c3ec494..98952637e 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -49,7 +49,7 @@ Ruleset.prototype.eval = function (context) { } var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(Ruleset)(selectors, rules, this.strictImports), + ruleset = new Ruleset(selectors, rules, this.strictImports), rule, subRule; ruleset.originalRuleset = this; @@ -259,7 +259,7 @@ Ruleset.prototype.find = function (selector, self, filter) { if (match) { if (selector.elements.length > match) { if (!filter || filter(rule)) { - foundMixins = rule.find(new(Selector)(selector.elements.slice(match)), self, filter); + foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter); for (var i = 0; i < foundMixins.length; ++i) { foundMixins[i].path.push(rule); } @@ -485,7 +485,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) { // it is not lost if (sel.length > 0) { sel[0].elements = sel[0].elements.slice(0); - sel[0].elements.push(new(Element)(el.combinator, '', el.index, el.currentFileInfo)); + sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo)); } selectorsMultiplied.push(sel); } @@ -532,7 +532,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) { combinator = parentEl.combinator; } // join the elements so far with the first part of the parent - newJoinedSelector.elements.push(new(Element)(combinator, parentEl.value, el.index, el.currentFileInfo)); + newJoinedSelector.elements.push(new Element(combinator, parentEl.value, el.index, el.currentFileInfo)); newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); } @@ -572,7 +572,7 @@ Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) { var i, sel; if (selectors.length === 0) { - selectors.push([ new(Selector)(elements) ]); + selectors.push([ new Selector(elements) ]); return; } @@ -584,7 +584,7 @@ Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) { sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); } else { - sel.push(new(Selector)(elements)); + sel.push(new Selector(elements)); } } }; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index e2987b6fa..406130b8a 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -25,7 +25,7 @@ Selector.prototype.accept = function (visitor) { }; Selector.prototype.createDerived = function(elements, extendList, evaldCondition) { evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); + var newSelector = new Selector(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); newSelector.evaldCondition = evaldCondition; newSelector.mediaEmpty = this.mediaEmpty; return newSelector; diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index ee31c834a..38f2a2307 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -17,7 +17,7 @@ Value.prototype.eval = function (context) { if (this.value.length === 1) { return this.value[0].eval(context); } else { - return new(Value)(this.value.map(function (v) { + return new Value(this.value.map(function (v) { return v.eval(context); })); } diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index b50db18eb..b19dd2130 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -11,7 +11,7 @@ Variable.prototype.eval = function (context) { var variable, name = this.name; if (name.indexOf('@@') === 0) { - name = '@' + new(Variable)(name.slice(1), this.index, this.currentFileInfo).eval(context).value; + name = '@' + new Variable(name.slice(1), this.index, this.currentFileInfo).eval(context).value; } if (this.evaluating) { diff --git a/lib/less/visitors/import-visitor.js b/lib/less/visitors/import-visitor.js index d74ff0ed5..38b78fcaf 100644 --- a/lib/less/visitors/import-visitor.js +++ b/lib/less/visitors/import-visitor.js @@ -96,7 +96,7 @@ ImportVisitor.prototype = { if (!inlineCSS && (context.importMultiple || !duplicateImport)) { importVisitor.recursionDetector[fullPath] = true; - new(ImportVisitor)(importVisitor._importer, subFinish, context, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) + new ImportVisitor(importVisitor._importer, subFinish, context, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) .run(root); return; } From fde35b7c1c424b9ea5457147284fe633b06bfdc4 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 18:21:49 +0100 Subject: [PATCH 153/161] Fix default rootpath. Do not apply to sourcemap filename. Replace windows directory seperators. --- .gitignore | 1 + Gruntfile.js | 4 ++-- bin/lessc | 13 ++++++++++--- lib/less/source-map-output.js | 17 ++++++++++------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 55e880c0e..e1445711e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ npm-debug.log # project-specific tmp test/browser/less.js +test/sourcemaps/**/*.map test/sourcemaps/*.map test/sourcemaps/*.css diff --git a/Gruntfile.js b/Gruntfile.js index c2858b213..0958a51bf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -38,8 +38,8 @@ module.exports = function (grunt) { }, "sourcemap-test": { command: [ - 'node bin/lessc --source-map --source-map-map-inline test/less/import.less test/sourcemaps/import.css', - 'node bin/lessc --source-map --source-map-map-inline test/less/sourcemaps/basic.less test/sourcemaps/basic.css' + 'node bin/lessc --source-map=test/sourcemaps/maps/import-map.map test/less/import.less test/sourcemaps/import.css', + 'node bin/lessc --source-map test/less/sourcemaps/basic.less test/sourcemaps/basic.css' ].join('&&') } }, diff --git a/bin/lessc b/bin/lessc index fe65b4fd8..b4e3a4a3f 100755 --- a/bin/lessc +++ b/bin/lessc @@ -264,9 +264,8 @@ if (output) { } } -sourceMapOptions.sourceMapBasepath = options.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); - if (options.sourceMap) { + sourceMapOptions.sourceMapInputFilename = input; if (!sourceMapOptions.sourceMapFullFilename) { if (!output && !sourceMapFileInline) { @@ -293,6 +292,15 @@ if (options.sourceMap) { } } +sourceMapOptions.sourceMapBasepath = sourceMapOptions.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); + +if (!sourceMapOptions.sourceMapRootpath) { + var pathToMap = path.dirname(sourceMapFileInline ? output : sourceMapOptions.sourceMapFullFilename), + pathToInput = path.dirname(sourceMapOptions.sourceMapInputFilename); + sourceMapOptions.sourceMapRootpath = path.relative(pathToMap, pathToInput); +} + + if (! input) { console.log("lessc: no input files"); console.log(""); @@ -364,7 +372,6 @@ var parseLessFile = function (e, data) { if (options.lint) { options.sourceMap = false; } - sourceMapOptions.sourceMapRootpath = options.sourceMapRootpath || ""; sourceMapOptions.sourceMapFileInline = sourceMapFileInline; if (options.sourceMap) { diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 6c78a4763..8f5b45a07 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -5,21 +5,24 @@ module.exports = function (environment) { this._rootNode = options.rootNode; this._contentsMap = options.contentsMap; this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename; + this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/'); this._outputFilename = options.outputFilename; this.sourceMapURL = options.sourceMapURL; if (options.sourceMapBasepath) { this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); } - this._sourceMapRootpath = options.sourceMapRootpath; + if (options.sourceMapRootpath) { + this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/'); + if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { + this._sourceMapRootpath += '/'; + } + } else { + this._sourceMapRootpath = ""; + } this._outputSourceFiles = options.outputSourceFiles; this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator(); this._sourceMapFileInline = options.sourceMapFileInline; - if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { - this._sourceMapRootpath += '/'; - } - this._lineNumber = 0; this._column = 0; }; @@ -121,7 +124,7 @@ module.exports = function (environment) { if (this.sourceMapURL) { sourceMapURL = this.sourceMapURL; } else if (this._sourceMapFilename) { - sourceMapURL = this.normalizeFilename(this._sourceMapFilename); + sourceMapURL = this._sourceMapFilename; } this.sourceMapURL = sourceMapURL; From 9f82ece8fb13b51c5b2b4c7253b54e8efe1f5b42 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 18:40:12 +0100 Subject: [PATCH 154/161] sourcemap filename is optional so put a guard around the path replace --- lib/less/source-map-output.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 8f5b45a07..0d501e5ff 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -5,7 +5,9 @@ module.exports = function (environment) { this._rootNode = options.rootNode; this._contentsMap = options.contentsMap; this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; - this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/'); + if (options.sourceMapFilename) { + this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/'); + } this._outputFilename = options.outputFilename; this.sourceMapURL = options.sourceMapURL; if (options.sourceMapBasepath) { From d436b83c06239d50394cba275f2535adf68db6f4 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 18:42:07 +0100 Subject: [PATCH 155/161] put the built-in cache on the less object --- lib/less-browser/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js index 6cb35492e..0553155a6 100644 --- a/lib/less-browser/index.js +++ b/lib/less-browser/index.js @@ -27,7 +27,7 @@ less.FileManager = FileManager; require("./log-listener")(less, options); var errors = require("./error-reporting")(window, less, options); var browser = require("./browser"); -var cache = options.cache || require("./cache")(window, options, less.logger); +var cache = less.cache = options.cache || require("./cache")(window, options, less.logger); options.env = options.env || (window.location.hostname == '127.0.0.1' || window.location.hostname == '0.0.0.0' || From c3831e10d6a724c19e62abc8bbd8f5f147619f1d Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 18:48:40 +0100 Subject: [PATCH 156/161] ignore .grunt folder for npm --- .npmignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmignore b/.npmignore index 122bb69e2..744f6a065 100644 --- a/.npmignore +++ b/.npmignore @@ -1,8 +1,9 @@ .gitattributes build/ +.grunt/ benchmark/ test/ # re-include test files as they can be useful for plugins that do testing !test/*.js tmp/ -gradle/ \ No newline at end of file +gradle/ From 65702f80a331e315be940dc044315bc0767cc00b Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 19:09:23 +0100 Subject: [PATCH 157/161] make sure plugins can be loaded from the current directory if lessc is installed globally --- lib/less-node/plugin-loader.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/less-node/plugin-loader.js b/lib/less-node/plugin-loader.js index 06c70a7be..bef1d2292 100644 --- a/lib/less-node/plugin-loader.js +++ b/lib/less-node/plugin-loader.js @@ -1,3 +1,4 @@ +var path = require("path"); /** * Node Plugin Loader */ @@ -47,6 +48,25 @@ PluginLoader.prototype.versionToString = function(version) { return versionString; }; PluginLoader.prototype.tryRequirePlugin = function(name) { + // is at the same level as the less.js module + try { + return require("../../../" + name); + } + catch(e) { + } + // is installed as a sub dependency of the current folder + try { + return require(path.join(process.cwd(), "node_modules", name)); + } + catch(e) { + } + // is referenced relative to the current directory + try { + return require(path.join(process.cwd(), name)); + } + catch(e) { + } + // unlikely - would have to be a dependency of where this code was running (less.js)... if (name[0] !== '.') { try { return require(name); @@ -54,11 +74,6 @@ PluginLoader.prototype.tryRequirePlugin = function(name) { catch(e) { } } - try { - return require("../../../" + name); - } - catch(e) { - } }; PluginLoader.prototype.printUsage = function(plugins) { for(var i = 0; i < plugins.length; i++) { From e02449b656084d519528b4888cb1e36d79c87354 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 21:00:19 +0100 Subject: [PATCH 158/161] improve some of the source map options --- bin/lessc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/lessc b/bin/lessc index b4e3a4a3f..fe963d726 100755 --- a/bin/lessc +++ b/bin/lessc @@ -292,9 +292,11 @@ if (options.sourceMap) { } } -sourceMapOptions.sourceMapBasepath = sourceMapOptions.sourceMapBasepath || (input ? path.dirname(input) : process.cwd()); +if (sourceMapOptions.sourceMapBasepath === undefined) { + sourceMapOptions.sourceMapBasepath = input ? path.dirname(input) : process.cwd(); +} -if (!sourceMapOptions.sourceMapRootpath) { +if (sourceMapOptions.sourceMapRootpath === undefined) { var pathToMap = path.dirname(sourceMapFileInline ? output : sourceMapOptions.sourceMapFullFilename), pathToInput = path.dirname(sourceMapOptions.sourceMapInputFilename); sourceMapOptions.sourceMapRootpath = path.relative(pathToMap, pathToInput); From feea4f676fa498f76427d6b3ce0248a0acd6cd19 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 21:10:47 +0100 Subject: [PATCH 159/161] ignore other things in bower that are not needed --- bower.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bower.json b/bower.json index 6ad5b5de3..395a03198 100644 --- a/bower.json +++ b/bower.json @@ -1,18 +1,24 @@ { "name": "less", - "version": "2.0.0", + "version": "2.0.0-b1", "main": "./dist/less.js", "ignore": [ "**/.*", "benchmark", "bin", "build", + "gradle", "lib", "test", "*.md", "LICENSE", "Gruntfile.js", - "package.json", - "bower.json" + "*.json", + "*.yml", + "gradlew", + "gradlew.bat", + ".gitattributes", + ".jshintrc", + ".npmignore" ] } From 0fa3a9fe9c1265ebb58a4b131665d60a691562f2 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 21:12:05 +0100 Subject: [PATCH 160/161] change version to beta 1 --- lib/less-node/plugin-loader.js | 2 +- lib/less/index.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/less-node/plugin-loader.js b/lib/less-node/plugin-loader.js index bef1d2292..bacbac0e7 100644 --- a/lib/less-node/plugin-loader.js +++ b/lib/less-node/plugin-loader.js @@ -35,7 +35,7 @@ PluginLoader.prototype.tryLoadPlugin = function(name, argument) { PluginLoader.prototype.compareVersion = function(aVersion, bVersion) { for(var i = 0; i < aVersion.length; i++) { if (aVersion[i] !== bVersion[i]) { - return aVersion[i] > bVersion[i] ? -1 : 1; + return parseInt(aVersion[i]) > parseInt(bVersion[i]) ? -1 : 1; } } return 0; diff --git a/lib/less/index.js b/lib/less/index.js index 43546e062..d530d8885 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -2,7 +2,7 @@ module.exports = function(environment, fileManagers) { var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment; var less = { - version: [2, 0, 0], + version: [2, 0, "0-b1"], data: require('./data'), tree: require('./tree'), Environment: (Environment = require("./environment/environment")), diff --git a/package.json b/package.json index 49b9d2181..9859ccfed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "less", - "version": "2.0.0", + "version": "2.0.0-b1", "description": "Leaner CSS", "homepage": "http://lesscss.org", "author": { From 4a0d806f421082dfedba4e148123f216ba0d0645 Mon Sep 17 00:00:00 2001 From: Luke Page Date: Sun, 19 Oct 2014 21:43:24 +0100 Subject: [PATCH 161/161] build v2 beta and update changelog --- CHANGELOG.md | 27 +- bower.json | 2 +- dist/less.js | 9202 ++++++++++++++++++++++++++++++++++++++++++++++ dist/less.min.js | 16 + 4 files changed, 9237 insertions(+), 10 deletions(-) create mode 100644 dist/less.js create mode 100644 dist/less.min.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a708c01..12c5d7d21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,23 @@ -#2.0.0 +# 2.0.0-b1 -2014-??-?? +2014-10-19 + - Public Beta / Release Candidate - Feedback welcome + For a guide to breaking changes see [the v2 upgrade guide](lesscss.org/usage/#v2-upgrade-guide) - no longer including old versions of less in the repo or npm - - not including test and gradle files in npm (Note: TODO may need to move some test framework into lib for core plugins) + - not including test less and gradle files in npm - colours now output in the format they are added unless compressing, so yellow will output yellow, not its hex counterpart - - better parsing - better comment support (TODO) and comments in brackets can now contain comments including quotes. -TODO - - Environment Support - - Finalised Plugin Support + - better parsing - better comment support and comments in brackets can now contain comments including quotes. + - Removal of dependency on clean-css - install less-plugin-clean-css and use --clean-css to reference plugin + - Environment Support - less is now separate from its node and browser environment implementations and adding support for another javascript environment should be straight forward. + - Plugin Support - it is now straight forward to add AST manipulations (see less-plugin-inline-images), file managers (see less-plugin-npm-import) and post processors (see less-plugin-clean-css and less-plugin-autoprefix). + - We now recommend using less.render and using the parser directly is not in the same way as in v2. It is possible but it would require changes and we do not guarantee it will not be broken in minor version releases. + - In the browser, less.pageLoadFinished will be a promise, resolved when less has finished its initial processing. less.refresh and less.modifyVars also return promises. + - In the browser, as before less is used as options, however this is now copied to less.options if you need to access after less has run + - In the browser, the cache can be overwritten by setting less.cache before less loads. After load less.cache will be the default implementation. + - less.js now uses browserify to generate its browser side component + - default values for the sourcemap options have been re-done and improved to hopefully mean creating sourcemaps is easier + - Many smaller bugfixes and API changes. Please let us know if something you relied on has disappeared or an area should be better documented. # 1.7.5 @@ -22,7 +31,7 @@ TODO - updates to some dependencies - Fix interpolated import in media query - A few other various small corrections - + # 1.7.4 2014-07-27 @@ -49,7 +58,7 @@ TODO - Allow paths option to be a string (in 1.7.1 less started throwing an exception instead of incorrectly processing the string as an array of chars) - Do not round numbers when used with javascript (introduced 1.7.0) - + # 1.7.1 2014-06-08 diff --git a/bower.json b/bower.json index 395a03198..eb4120e39 100644 --- a/bower.json +++ b/bower.json @@ -21,4 +21,4 @@ ".jshintrc", ".npmignore" ] -} +} \ No newline at end of file diff --git a/dist/less.js b/dist/less.js new file mode 100644 index 000000000..6c2e79af6 --- /dev/null +++ b/dist/less.js @@ -0,0 +1,9202 @@ +/*! + * Less - Leaner CSS v2.0.0-b1 + * http://lesscss.org + * + * Copyright (c) 2009-2014, Alexis Sellier + * Licensed under the Apache v2 License. + * + */ + + /** * @license Apache v2 + */ + +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && css.childNodes.length > 0 && + oldCss.firstChild.nodeValue === css.firstChild.nodeValue); + } + + var head = document.getElementsByTagName('head')[0]; + + // If there is no oldCss, just append; otherwise, only append if we need + // to replace oldCss with an updated stylesheet + if (oldCss === null || keepOldCss === false) { + var nextEl = sheet && sheet.nextSibling || null; + if (nextEl) { + nextEl.parentNode.insertBefore(css, nextEl); + } else { + head.appendChild(css); + } + } + if (oldCss && keepOldCss === false) { + oldCss.parentNode.removeChild(oldCss); + } + + // For IE. + // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. + // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head + if (css.styleSheet) { + try { + css.styleSheet.cssText = styles; + } catch (e) { + throw new Error("Couldn't reassign styleSheet.cssText."); + } + } + } +}; + +},{"./utils":7}],2:[function(require,module,exports){ +// Cache system is a bit outdated and could do with work + +module.exports = function(window, options, logger) { + var cache = null; + if (options.env !== 'development') { + try { + cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; + } catch (_) {} + } + return { + setCSS: function(path, lastModified, styles) { + if (cache) { + logger.info('saving ' + path+ ' to cache.'); + try { + cache.setItem(path, styles); + cache.setItem(path + ':timestamp', lastModified); + } catch(e) { + //TODO - could do with adding more robust error handling + logger.error('failed to save'); + } + } + }, + getCSS: function(path, webInfo) { + var css = cache && cache.getItem(path), + timestamp = cache && cache.getItem(path + ':timestamp'); + + if (timestamp && webInfo.lastModified && + (new Date(webInfo.lastModified).valueOf() === + new Date(timestamp).valueOf())) { + // Use local copy + return css; + } + } + }; +}; + +},{}],3:[function(require,module,exports){ +var utils = require("./utils"), + browser = require("./browser"); + +module.exports = function(window, less, options) { + + function errorHTML(e, rootHref) { + var id = 'less-error-message:' + utils.extractId(rootHref || ""); + var template = '
  • {content}
  • '; + var elem = window.document.createElement('div'), timer, content, errors = []; + var filename = e.filename || rootHref; + var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1]; + + elem.id = id; + elem.className = "less-error-message"; + + content = '

    ' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + + '

    ' + '

    in ' + filenameNoPath + " "; + + var errorline = function (e, i, classname) { + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) + .replace(/\{class\}/, classname) + .replace(/\{content\}/, e.extract[i])); + } + }; + + if (e.extract) { + errorline(e, 0, ''); + errorline(e, 1, 'line'); + errorline(e, 2, ''); + content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

    ' + + '
      ' + errors.join('') + '
    '; + } + if (e.stack && (e.extract || options.logLevel >= 4)) { + content += '
    Stack Trace
    ' + e.stack.split('\n').slice(1).join('
    '); + } + elem.innerHTML = content; + + // CSS for error messages + browser.createCSS(window.document, [ + '.less-error-message ul, .less-error-message li {', + 'list-style-type: none;', + 'margin-right: 15px;', + 'padding: 4px 0;', + 'margin: 0;', + '}', + '.less-error-message label {', + 'font-size: 12px;', + 'margin-right: 15px;', + 'padding: 4px 0;', + 'color: #cc7777;', + '}', + '.less-error-message pre {', + 'color: #dd6666;', + 'padding: 4px 0;', + 'margin: 0;', + 'display: inline-block;', + '}', + '.less-error-message pre.line {', + 'color: #ff0000;', + '}', + '.less-error-message h3 {', + 'font-size: 20px;', + 'font-weight: bold;', + 'padding: 15px 0 5px 0;', + 'margin: 0;', + '}', + '.less-error-message a {', + 'color: #10a', + '}', + '.less-error-message .error {', + 'color: red;', + 'font-weight: bold;', + 'padding-bottom: 2px;', + 'border-bottom: 1px dashed red;', + '}' + ].join('\n'), { title: 'error-message' }); + + elem.style.cssText = [ + "font-family: Arial, sans-serif", + "border: 1px solid #e00", + "background-color: #eee", + "border-radius: 5px", + "-webkit-border-radius: 5px", + "-moz-border-radius: 5px", + "color: #e00", + "padding: 15px", + "margin-bottom: 15px" + ].join(';'); + + if (options.env === 'development') { + timer = setInterval(function () { + var document = window.document, + body = document.body; + if (body) { + if (document.getElementById(id)) { + body.replaceChild(elem, document.getElementById(id)); + } else { + body.insertBefore(elem, body.firstChild); + } + clearInterval(timer); + } + }, 10); + } + } + + function error(e, rootHref) { + if (!options.errorReporting || options.errorReporting === "html") { + errorHTML(e, rootHref); + } else if (options.errorReporting === "console") { + errorConsole(e, rootHref); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("add", e, rootHref); + } + } + + function removeErrorHTML(path) { + var node = window.document.getElementById('less-error-message:' + utils.extractId(path)); + if (node) { + node.parentNode.removeChild(node); + } + } + + function removeErrorConsole(path) { + //no action + } + + function removeError(path) { + if (!options.errorReporting || options.errorReporting === "html") { + removeErrorHTML(path); + } else if (options.errorReporting === "console") { + removeErrorConsole(path); + } else if (typeof options.errorReporting === 'function') { + options.errorReporting("remove", path); + } + } + + function errorConsole(e, rootHref) { + var template = '{line} {content}'; + var filename = e.filename || rootHref; + var errors = []; + var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') + + " in " + filename + " "; + + var errorline = function (e, i, classname) { + if (e.extract[i] !== undefined) { + errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1)) + .replace(/\{class\}/, classname) + .replace(/\{content\}/, e.extract[i])); + } + }; + + if (e.extract) { + errorline(e, 0, ''); + errorline(e, 1, 'line'); + errorline(e, 2, ''); + content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' + + errors.join('\n'); + } + if (e.stack && (e.extract || options.logLevel >= 4)) { + content += '\nStack Trace\n' + e.stack; + } + less.logger.error(content); + } + + return { + add: error, + remove: removeError + }; +}; + +},{"./browser":1,"./utils":7}],4:[function(require,module,exports){ +/*global window, XMLHttpRequest */ + +module.exports = function(options, isFileProtocol, logger) { + +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, + AbstractFileManager = require("../less/environment/abstract-file-manager.js"); + +var fileCache = {}; + +//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load +// isFileProtocol is global + +function getXMLHttpRequest() { + if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) { + return new XMLHttpRequest(); + } else { + try { + /*global ActiveXObject */ + return new ActiveXObject("Microsoft.XMLHTTP"); + } catch (e) { + logger.error("browser doesn't support AJAX."); + return null; + } + } +} + +var FileManager = function() { +}; + +FileManager.prototype = new AbstractFileManager(); + +FileManager.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() { + return true; +}; +FileManager.prototype.join = function join(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return this.extractUrlParts(laterPath, basePath).path; +}; +FileManager.prototype.doXHR = function doXHR(url, type, callback, errback) { + + var xhr = getXMLHttpRequest(); + var async = isFileProtocol ? options.fileAsync : options.async; + + if (typeof(xhr.overrideMimeType) === 'function') { + xhr.overrideMimeType('text/css'); + } + logger.debug("XHR: Getting '" + url + "'"); + xhr.open('GET', url, async); + xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); + xhr.send(null); + + function handleResponse(xhr, callback, errback) { + if (xhr.status >= 200 && xhr.status < 300) { + callback(xhr.responseText, + xhr.getResponseHeader("Last-Modified")); + } else if (typeof(errback) === 'function') { + errback(xhr.status, url); + } + } + + if (isFileProtocol && !options.fileAsync) { + if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) { + callback(xhr.responseText); + } else { + errback(xhr.status, url); + } + } else if (async) { + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + handleResponse(xhr, callback, errback); + } + }; + } else { + handleResponse(xhr, callback, errback); + } +}; +FileManager.prototype.supports = function(filename, currentDirectory, options, environment) { + return true; +}; + +FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment) { + return new PromiseConstructor(function(fullfill, reject) { + if (currentDirectory && !this.isPathAbsolute(filename)) { + filename = currentDirectory + filename; + } + + options = options || {}; + + // sheet may be set to the stylesheet for the initial load or a collection of properties including + // some context variables for imports + var hrefParts = this.extractUrlParts(filename, window.location.href); + var href = hrefParts.url; + + if (options.useFileCache && fileCache[href]) { + try { + var lessText = fileCache[href]; + fullfill({ contents: lessText, filename: href, webInfo: { lastModified: new Date() }}); + } catch (e) { + reject({filename: href, message: "Error loading file " + href + " error was " + e.message}); + } + return; + } + + this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) { + // per file cache + fileCache[href] = data; + + // Use remote copy (re-parse) + fullfill({ contents: data, filename: href, webInfo: { lastModified: lastModified }}); + }, function doXHRError(status, url) { + reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href }); + }); + }.bind(this)); +}; + +return FileManager; +}; + +},{"../less/environment/abstract-file-manager.js":12,"promise":undefined}],5:[function(require,module,exports){ +// +// browser.js - client-side engine +// +/*global window, document, location */ + +var less; + +/* + TODO - options is now hidden - we should expose it on the less object, but not have it "as" the less object + less = { fileAsync: true } + then access as less.environment.options.fileAsync ? + */ + +var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(window.location.protocol), + options = window.less || {}; + +// shim Promise if required +require('promise/polyfill.js'); + +window.less = less = require('../less')(); +var environment = less.environment, + FileManager = require("./file-manager")(options, isFileProtocol, less.logger), + fileManager = new FileManager(); +environment.addFileManager(fileManager); +less.FileManager = FileManager; + +require("./log-listener")(less, options); +var errors = require("./error-reporting")(window, less, options); +var browser = require("./browser"); +var cache = less.cache = options.cache || require("./cache")(window, options, less.logger); + +options.env = options.env || (window.location.hostname == '127.0.0.1' || + window.location.hostname == '0.0.0.0' || + window.location.hostname == 'localhost' || + (window.location.port && + window.location.port.length > 0) || + isFileProtocol ? 'development' + : 'production'); + +// Load styles asynchronously (default: false) +// +// This is set to `false` by default, so that the body +// doesn't start loading before the stylesheets are parsed. +// Setting this to `true` can result in flickering. +// +options.async = options.async || false; +options.fileAsync = options.fileAsync || false; + +// Interval between watch polls +options.poll = options.poll || (isFileProtocol ? 1000 : 1500); + +//Setup user functions +if (options.functions) { + less.functions.functionRegistry.addMultiple(options.functions); +} + +var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash); +if (dumpLineNumbers) { + options.dumpLineNumbers = dumpLineNumbers[1]; +} + +var typePattern = /^text\/(x-)?less$/; + +function postProcessCSS(styles) { + if (options.postProcessor && typeof options.postProcessor === 'function') { + styles = options.postProcessor.call(styles, styles) || styles; + } + return styles; +} + +function clone(obj) { + var cloned = {}; + for(var prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + return cloned; +} + +// only really needed for phantom +function bind(func, thisArg) { + var curryArgs = Array.prototype.slice.call(arguments, 2); + return function() { + var args = curryArgs.concat(Array.prototype.slice.call(arguments, 0)); + return func.apply(thisArg, args); + }; +} + +function loadStyles(modifyVars) { + var styles = document.getElementsByTagName('style'), + style; + + for (var i = 0; i < styles.length; i++) { + style = styles[i]; + if (style.type.match(typePattern)) { + var instanceOptions = clone(options); + instanceOptions.modifyVars = modifyVars; + var lessText = style.innerHTML || ''; + instanceOptions.filename = document.location.href.replace(/#.*$/, ''); + + if (modifyVars || instanceOptions.globalVars) { + instanceOptions.useFileCache = true; + } + + /*jshint loopfunc:true */ + // use closure to store current style + less.render(lessText, instanceOptions) + .then(bind(function(style, result) { + style.type = 'text/css'; + if (style.styleSheet) { + style.styleSheet.cssText = result.css; + } else { + style.innerHTML = result.css; + } + }, null, style), + function(e) { + errors.add(e, "inline"); + }); + } + } +} + +function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) { + + var instanceOptions = clone(options); + instanceOptions.mime = sheet.type; + + if (modifyVars) { + instanceOptions.modifyVars = modifyVars; + } + if (modifyVars || options.globalVars) { + instanceOptions.useFileCache = true; + } + + fileManager.loadFile(sheet.href, null, instanceOptions, environment) + .then(function loadInitialFileCallback(loadedFile) { + + var data = loadedFile.contents, + path = loadedFile.filename, + webInfo = loadedFile.webInfo; + + var newFileInfo = { + currentDirectory: fileManager.getPath(path), + filename: path, + rootFilename: path, + relativeUrls: instanceOptions.relativeUrls}; + + newFileInfo.entryPath = newFileInfo.currentDirectory; + newFileInfo.rootpath = instanceOptions.rootpath || newFileInfo.currentDirectory; + + if (webInfo) { + webInfo.remaining = remaining; + + var css = cache.getCSS(path, webInfo); + if (!reload && css) { + browser.createCSS(window.document, css, sheet); + webInfo.local = true; + callback(null, null, data, sheet, webInfo, path); + return; + } + } + + //TODO add tests around how this behaves when reloading + errors.remove(path); + + instanceOptions.rootFileInfo = newFileInfo; + less.render(data, instanceOptions) + .then(function(result) { + callback(null, result.css, data, sheet, webInfo, path); + }, + function(e) { + e.href = path; + callback(e); + }); + }, + function(e) { + callback(e); + }); +} + +function loadStyleSheets(callback, reload, modifyVars) { + for (var i = 0; i < less.sheets.length; i++) { + loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars); + } +} + +function initRunningMode(){ + if (less.env === 'development') { + less.watchTimer = setInterval(function () { + if (less.watchMode) { + loadStyleSheets(function (e, css, _, sheet, context) { + if (e) { + errors.add(e, e.href || sheet.href); + } else if (css) { + css = postProcessCSS(css); + browser.createCSS(window.document, css, sheet); + cache.setCSS(sheet.href, context.lastModified, css); + } + }); + } + }, options.poll); + } +} + +// +// Watch mode +// +less.watch = function () { + if (!less.watchMode ){ + less.env = 'development'; + initRunningMode(); + } + this.watchMode = true; + return true; +}; + +less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; }; + +if (/!watch/.test(location.hash)) { + less.watch(); +} + +// +// Get all tags with the 'rel' attribute set to "stylesheet/less" +// +less.registerStylesheets = function() { + return new Promise(function(resolve, reject) { + var links = document.getElementsByTagName('link'); + less.sheets = []; + + for (var i = 0; i < links.length; i++) { + if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && + (links[i].type.match(typePattern)))) { + less.sheets.push(links[i]); + } + } + + resolve(); + }); +}; + +// +// With this function, it's possible to alter variables and re-render +// CSS without reloading less-files +// +less.modifyVars = function(record) { + return less.refresh(false, record); +}; + +less.refresh = function (reload, modifyVars) { + return new Promise(function (resolve, reject) { + var startTime, endTime, totalMilliseconds; + startTime = endTime = new Date(); + + loadStyleSheets(function (e, css, _, sheet, webInfo) { + if (e) { + errors.add(e, e.href || sheet.href); + reject(e); + } + if (webInfo.local) { + less.logger.info("loading " + sheet.href + " from cache."); + } else { + less.logger.info("rendered " + sheet.href + " successfully."); + css = postProcessCSS(css); + browser.createCSS(window.document, css, sheet); + cache.setCSS(sheet.href, webInfo.lastModified, css); + } + less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms'); + if (webInfo.remaining === 0) { + totalMilliseconds = new Date() - startTime; + less.logger.info("less has finished. css generated in " + totalMilliseconds + 'ms'); + resolve({ + startTime: startTime, + endTime: endTime, + totalMilliseconds: totalMilliseconds, + sheets: less.sheets.length + }); + } + endTime = new Date(); + }, reload, modifyVars); + + loadStyles(modifyVars); + }); +}; + +less.refreshStyles = loadStyles; + +less.pageLoadFinished = less.registerStylesheets().then( + function () { + return less.refresh(less.env === 'development'); + } +); + +},{"../less":27,"./browser":1,"./cache":2,"./error-reporting":3,"./file-manager":4,"./log-listener":6,"promise/polyfill.js":undefined}],6:[function(require,module,exports){ +module.exports = function(less, options) { + + var logLevel_debug = 4, + logLevel_info = 3, + logLevel_warn = 2, + logLevel_error = 1; + + // The amount of logging in the javascript console. + // 3 - Debug, information and errors + // 2 - Information and errors + // 1 - Errors + // 0 - None + // Defaults to 2 + options.logLevel = typeof(options.logLevel) !== 'undefined' ? options.logLevel : (options.env === 'development' ? logLevel_info : logLevel_error); + + if (!options.loggers) { + options.loggers = [{ + debug: function(msg) { + if (options.logLevel >= logLevel_debug) { + console.log(msg); + } + }, + info: function(msg) { + if (options.logLevel >= logLevel_info) { + console.log(msg); + } + }, + warn: function(msg) { + if (options.logLevel >= logLevel_warn) { + console.warn(msg); + } + }, + error: function(msg) { + if (options.logLevel >= logLevel_error) { + console.error(msg); + } + } + }]; + } + for(var i = 0; i < options.loggers.length; i++) { + less.logger.addListener(options.loggers[i]); + } +}; + +},{}],7:[function(require,module,exports){ +module.exports = { + extractId: function(href) { + return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain + .replace(/^\//, '' ) // Remove root / + .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension + .replace(/[^\.\w-]+/g, '-') // Replace illegal characters + .replace(/\./g, ':'); // Replace dots with colons(for valid id) + } +}; + +},{}],8:[function(require,module,exports){ +var contexts = {}; +module.exports = contexts; + +var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) { + if (!original) { return; } + + for(var i = 0; i < propertiesToCopy.length; i++) { + if (original.hasOwnProperty(propertiesToCopy[i])) { + destination[propertiesToCopy[i]] = original[propertiesToCopy[i]]; + } + } +}; + +/* + parse is used whilst parsing + */ +var parseCopyProperties = [ + // options + 'paths', // option - unmodified - paths to search for imports on + 'relativeUrls', // option - whether to adjust URL's to be relative + 'rootpath', // option - rootpath to append to URL's + 'strictImports', // option - + 'insecure', // option - whether to allow imports from insecure ssl hosts + 'dumpLineNumbers', // option - whether to dump line numbers + 'compress', // option - whether to compress + 'syncImport', // option - whether to import synchronously + 'chunkInput', // option - whether to chunk input. more performant but causes parse issues. + 'mime', // browser only - mime type for sheet import + 'useFileCache', // browser only - whether to use the per file session cache + // context + 'processImports', // option & context - whether to process imports. if false then imports will not be imported. + // Used by the import manager to stop multiple import visitors being created. + 'reference', // Used to indicate that the contents are imported by reference + 'pluginManager' // Used as the plugin manager for the session +]; + +contexts.Parse = function(options) { + copyFromOriginal(options, this, parseCopyProperties); + + if (typeof this.paths === "string") { this.paths = [this.paths]; } +}; + +var evalCopyProperties = [ + 'compress', // whether to compress + 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri) + 'strictMath', // whether math has to be within parenthesis + 'strictUnits', // whether units need to evaluate correctly + 'sourceMap', // whether to output a source map + 'importMultiple', // whether we are currently importing multiple copies + 'urlArgs', // whether to add args into url tokens + 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true + 'pluginManager' // Used as the plugin manager for the session + ]; + +contexts.Eval = function(options, frames) { + copyFromOriginal(options, this, evalCopyProperties); + + this.frames = frames || []; +}; + +contexts.Eval.prototype.inParenthesis = function () { + if (!this.parensStack) { + this.parensStack = []; + } + this.parensStack.push(true); +}; + +contexts.Eval.prototype.outOfParenthesis = function () { + this.parensStack.pop(); +}; + +contexts.Eval.prototype.isMathOn = function () { + return this.strictMath ? (this.parensStack && this.parensStack.length) : true; +}; + +contexts.Eval.prototype.isPathRelative = function (path) { + return !/^(?:[a-z-]+:|\/)/.test(path); +}; + +contexts.Eval.prototype.normalizePath = function( path ) { + var + segments = path.split("/").reverse(), + segment; + + path = []; + while (segments.length !== 0 ) { + segment = segments.pop(); + switch( segment ) { + case ".": + break; + case "..": + if ((path.length === 0) || (path[path.length - 1] === "..")) { + path.push( segment ); + } else { + path.pop(); + } + break; + default: + path.push( segment ); + break; + } + } + + return path.join("/"); +}; + +//todo - do the same for the toCSS ? + + +},{}],9:[function(require,module,exports){ +module.exports = { + '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' +}; +},{}],10:[function(require,module,exports){ +module.exports = { + colors: require("./colors"), + unitConversions: require("./unit-conversions") +}; + +},{"./colors":9,"./unit-conversions":11}],11:[function(require,module,exports){ +module.exports = { + length: { + 'm': 1, + 'cm': 0.01, + 'mm': 0.001, + 'in': 0.0254, + 'px': 0.0254 / 96, + 'pt': 0.0254 / 72, + 'pc': 0.0254 / 72 * 12 + }, + duration: { + 's': 1, + 'ms': 0.001 + }, + angle: { + 'rad': 1/(2*Math.PI), + 'deg': 1/360, + 'grad': 1/400, + 'turn': 1 + } +}; +},{}],12:[function(require,module,exports){ +var abstractFileManager = function() { +}; + +abstractFileManager.prototype.getPath = function (filename) { + var j = filename.lastIndexOf('/'); + if (j < 0) { + j = filename.lastIndexOf('\\'); + } + if (j < 0) { + return ""; + } + return filename.slice(0, j + 1); +}; + +abstractFileManager.prototype.supportsSync = function() { + return false; +}; + +abstractFileManager.prototype.alwaysMakePathsAbsolute = function() { + return false; +}; + +abstractFileManager.prototype.isPathAbsolute = function(filename) { + return (/^(?:[a-z-]+:|\/|\\)/i).test(filename); +}; + +abstractFileManager.prototype.join = function(basePath, laterPath) { + if (!basePath) { + return laterPath; + } + return basePath + laterPath; +}; +abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) { + // diff between two paths to create a relative path + + var urlParts = this.extractUrlParts(url), + baseUrlParts = this.extractUrlParts(baseUrl), + i, max, urlDirectories, baseUrlDirectories, diff = ""; + if (urlParts.hostPart !== baseUrlParts.hostPart) { + return ""; + } + max = Math.max(baseUrlParts.directories.length, urlParts.directories.length); + for(i = 0; i < max; i++) { + if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; } + } + baseUrlDirectories = baseUrlParts.directories.slice(i); + urlDirectories = urlParts.directories.slice(i); + for(i = 0; i < baseUrlDirectories.length-1; i++) { + diff += "../"; + } + for(i = 0; i < urlDirectories.length-1; i++) { + diff += urlDirectories[i] + "/"; + } + return diff; +}; +// helper function, not part of API +abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) { + // urlParts[1] = protocol&hostname || / + // urlParts[2] = / if path relative to host base + // urlParts[3] = directories + // urlParts[4] = filename + // urlParts[5] = parameters + + var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i, + urlParts = url.match(urlPartsRegex), + returner = {}, directories = [], i, baseUrlParts; + + if (!urlParts) { + throw new Error("Could not parse sheet href - '" + url + "'"); + } + + // Stylesheets in IE don't always return the full path + if (baseUrl && (!urlParts[1] || urlParts[2])) { + baseUrlParts = baseUrl.match(urlPartsRegex); + if (!baseUrlParts) { + throw new Error("Could not parse page url - '"+baseUrl+"'"); + } + urlParts[1] = urlParts[1] || baseUrlParts[1] || ""; + if (!urlParts[2]) { + urlParts[3] = baseUrlParts[3] + urlParts[3]; + } + } + + if (urlParts[3]) { + directories = urlParts[3].replace(/\\/g, "/").split("/"); + + // extract out . before .. so .. doesn't absorb a non-directory + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".") { + directories.splice(i, 1); + i -= 1; + } + } + + for(i = 0; i < directories.length; i++) { + if (directories[i] === ".." && i > 0) { + directories.splice(i-1, 2); + i -= 2; + } + } + } + + returner.hostPart = urlParts[1]; + returner.directories = directories; + returner.path = (urlParts[1] || "") + directories.join("/"); + returner.fileUrl = returner.path + (urlParts[4] || ""); + returner.url = returner.fileUrl + (urlParts[5] || ""); + return returner; +}; + +module.exports = abstractFileManager; + +},{}],13:[function(require,module,exports){ +var environment = function(externalEnvironment, fileManagers) { + this.fileManagers = fileManagers || []; + externalEnvironment = externalEnvironment || {}; + + var optionalFunctions = ["encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"], + requiredFunctions = [], + functions = requiredFunctions.concat(optionalFunctions); + + for(var i = 0; i < functions.length; i++) { + var propName = functions[i], + environmentFunc = externalEnvironment[propName]; + if (environmentFunc) { + this[propName] = environmentFunc.bind(externalEnvironment); + } else if (i < requiredFunctions.length) { + this.warn("missing required function in environment - " + propName); + } + } +}; + +environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) { + var fileManagers = this.fileManagers; + if (options.pluginManager) { + fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers()); + } + for(var i = fileManagers.length - 1; i >= 0 ; i--) { + var fileManager = fileManagers[i]; + if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) { + return fileManager; + } + } + return null; +}; + +environment.prototype.addFileManager = function (fileManager) { + this.fileManagers.push(fileManager); +}; + +environment.prototype.clearFileManagers = function () { + this.fileManagers = []; +}; + +module.exports = environment; + +},{}],14:[function(require,module,exports){ +var Color = require("../tree/color"), + functionRegistry = require("./function-registry"); + +// Color Blending +// ref: http://www.w3.org/TR/compositing-1 + +function colorBlend(mode, color1, color2) { + var ab = color1.alpha, cb, // backdrop + as = color2.alpha, cs, // source + ar, cr, r = []; // result + + ar = as + ab * (1 - as); + for (var i = 0; i < 3; i++) { + cb = color1.rgb[i] / 255; + cs = color2.rgb[i] / 255; + cr = mode(cb, cs); + if (ar) { + cr = (as * cs + ab * (cb - + as * (cb + cs - cr))) / ar; + } + r[i] = cr * 255; + } + + return new Color(r, ar); +} + +var colorBlendModeFunctions = { + multiply: function(cb, cs) { + return cb * cs; + }, + screen: function(cb, cs) { + return cb + cs - cb * cs; + }, + overlay: function(cb, cs) { + cb *= 2; + return (cb <= 1) + ? colorBlendModeFunctions.multiply(cb, cs) + : colorBlendModeFunctions.screen(cb - 1, cs); + }, + softlight: function(cb, cs) { + var d = 1, e = cb; + if (cs > 0.5) { + e = 1; + d = (cb > 0.25) ? Math.sqrt(cb) + : ((16 * cb - 12) * cb + 4) * cb; + } + return cb - (1 - 2 * cs) * e * (d - cb); + }, + hardlight: function(cb, cs) { + return colorBlendModeFunctions.overlay(cs, cb); + }, + difference: function(cb, cs) { + return Math.abs(cb - cs); + }, + exclusion: function(cb, cs) { + return cb + cs - 2 * cb * cs; + }, + + // non-w3c functions: + average: function(cb, cs) { + return (cb + cs) / 2; + }, + negation: function(cb, cs) { + return 1 - Math.abs(cb + cs - 1); + } +}; + +for (var f in colorBlendModeFunctions) { + if (colorBlendModeFunctions.hasOwnProperty(f)) { + colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]); + } +} + +functionRegistry.addMultiple(colorBlend); + +},{"../tree/color":44,"./function-registry":19}],15:[function(require,module,exports){ +var Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + functionRegistry = require("./function-registry"), + colorFunctions; + +function clamp(val) { + return Math.min(1, Math.max(0, val)); +} +function hsla(color) { + return colorFunctions.hsla(color.h, color.s, color.l, color.a); +} +function number(n) { + if (n instanceof Dimension) { + return parseFloat(n.unit.is('%') ? n.value / 100 : n.value); + } else if (typeof(n) === 'number') { + return n; + } else { + throw { + type: "Argument", + message: "color functions take numbers as parameters" + }; + } +} +function scaled(n, size) { + if (n instanceof Dimension && n.unit.is('%')) { + return parseFloat(n.value * size / 100); + } else { + return number(n); + } +} +colorFunctions = { + rgb: function (r, g, b) { + return colorFunctions.rgba(r, g, b, 1.0); + }, + rgba: function (r, g, b, a) { + var rgb = [r, g, b].map(function (c) { return scaled(c, 255); }); + a = number(a); + return new Color(rgb, a); + }, + hsl: function (h, s, l) { + return colorFunctions.hsla(h, s, l, 1.0); + }, + hsla: function (h, s, l, a) { + function hue(h) { + 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; } + } + + h = (number(h) % 360) / 360; + s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a)); + + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + return colorFunctions.rgba(hue(h + 1/3) * 255, + hue(h) * 255, + hue(h - 1/3) * 255, + a); + }, + + hsv: function(h, s, v) { + return colorFunctions.hsva(h, s, v, 1.0); + }, + + hsva: function(h, s, v, a) { + h = ((number(h) % 360) / 360) * 360; + s = number(s); v = number(v); a = number(a); + + var i, f; + i = Math.floor((h / 60) % 6); + f = (h / 60) - i; + + var vs = [v, + v * (1 - s), + v * (1 - f * s), + v * (1 - (1 - f) * s)]; + var perm = [[0, 3, 1], + [2, 0, 1], + [1, 0, 3], + [1, 2, 0], + [3, 1, 0], + [0, 1, 2]]; + + return colorFunctions.rgba(vs[perm[i][0]] * 255, + vs[perm[i][1]] * 255, + vs[perm[i][2]] * 255, + a); + }, + + hue: function (color) { + return new Dimension(color.toHSL().h); + }, + saturation: function (color) { + return new Dimension(color.toHSL().s * 100, '%'); + }, + lightness: function (color) { + return new Dimension(color.toHSL().l * 100, '%'); + }, + hsvhue: function(color) { + return new Dimension(color.toHSV().h); + }, + hsvsaturation: function (color) { + return new Dimension(color.toHSV().s * 100, '%'); + }, + hsvvalue: function (color) { + return new Dimension(color.toHSV().v * 100, '%'); + }, + red: function (color) { + return new Dimension(color.rgb[0]); + }, + green: function (color) { + return new Dimension(color.rgb[1]); + }, + blue: function (color) { + return new Dimension(color.rgb[2]); + }, + alpha: function (color) { + return new Dimension(color.toHSL().a); + }, + luma: function (color) { + return new Dimension(color.luma() * color.alpha * 100, '%'); + }, + luminance: function (color) { + var luminance = + (0.2126 * color.rgb[0] / 255) + + (0.7152 * color.rgb[1] / 255) + + (0.0722 * color.rgb[2] / 255); + + return new Dimension(luminance * color.alpha * 100, '%'); + }, + saturate: function (color, amount) { + // filter: saturate(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + var hsl = color.toHSL(); + + hsl.s += amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + desaturate: function (color, amount) { + var hsl = color.toHSL(); + + hsl.s -= amount.value / 100; + hsl.s = clamp(hsl.s); + return hsla(hsl); + }, + lighten: function (color, amount) { + var hsl = color.toHSL(); + + hsl.l += amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + darken: function (color, amount) { + var hsl = color.toHSL(); + + hsl.l -= amount.value / 100; + hsl.l = clamp(hsl.l); + return hsla(hsl); + }, + fadein: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a += amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fadeout: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a -= amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + fade: function (color, amount) { + var hsl = color.toHSL(); + + hsl.a = amount.value / 100; + hsl.a = clamp(hsl.a); + return hsla(hsl); + }, + spin: function (color, amount) { + var hsl = color.toHSL(); + var hue = (hsl.h + amount.value) % 360; + + hsl.h = hue < 0 ? 360 + hue : hue; + + return hsla(hsl); + }, + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + mix: function (color1, color2, weight) { + if (!weight) { + weight = new Dimension(50); + } + var p = weight.value / 100.0; + var w = p * 2 - 1; + var a = color1.toHSL().a - color2.toHSL().a; + + var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2, + color1.rgb[1] * w1 + color2.rgb[1] * w2, + color1.rgb[2] * w1 + color2.rgb[2] * w2]; + + var alpha = color1.alpha * p + color2.alpha * (1 - p); + + return new Color(rgb, alpha); + }, + greyscale: function (color) { + return colorFunctions.desaturate(color, new Dimension(100)); + }, + contrast: function (color, dark, light, threshold) { + // filter: contrast(3.2); + // should be kept as is, so check for color + if (!color.rgb) { + return null; + } + if (typeof light === 'undefined') { + light = colorFunctions.rgba(255, 255, 255, 1.0); + } + if (typeof dark === 'undefined') { + dark = colorFunctions.rgba(0, 0, 0, 1.0); + } + //Figure out which is actually light and dark! + if (dark.luma() > light.luma()) { + var t = light; + light = dark; + dark = t; + } + if (typeof threshold === 'undefined') { + threshold = 0.43; + } else { + threshold = number(threshold); + } + if (color.luma() < threshold) { + return light; + } else { + return dark; + } + }, + argb: function (color) { + return new Anonymous(color.toARGB()); + }, + color: function(c) { + if ((c instanceof Quoted) && + (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) { + return new Color(c.value.slice(1)); + } + if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) { + c.keyword = undefined; + return c; + } + throw { + type: "Argument", + message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" + }; + }, + tint: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount); + }, + shade: function(color, amount) { + return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount); + } +}; +functionRegistry.addMultiple(colorFunctions); + +},{"../tree/anonymous":40,"../tree/color":44,"../tree/dimension":50,"../tree/quoted":67,"./function-registry":19}],16:[function(require,module,exports){ +module.exports = function(environment) { + var Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + functionRegistry = require("./function-registry"), + fallback = function(functionThis, node) { + return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context); + }, + logger = require('../logger'); + + functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) { + + var mimetype = mimetypeNode.value; + var filePath = (filePathNode && filePathNode.value); + + var fileManager = environment.getFileManager(filePath, this.context.currentFileInfo, this.context, environment, true); + + if (!fileManager) { + return fallback(this, filePathNode || mimetypeNode); + } + + var useBase64 = false; + + if (arguments.length < 2) { + filePath = mimetype; + } + + var fragmentStart = filePath.indexOf('#'); + var fragment = ''; + if (fragmentStart!==-1) { + fragment = filePath.slice(fragmentStart); + filePath = filePath.slice(0, fragmentStart); + } + + var currentDirectory = this.currentFileInfo.relativeUrls ? + this.currentFileInfo.currentDirectory : this.currentFileInfo.entryPath; + + // detect the mimetype if not given + if (arguments.length < 2) { + + mimetype = environment.mimeLookup(filePath); + + // use base 64 unless it's an ASCII or UTF-8 format + var charset = environment.charsetLookup(mimetype); + useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; + if (useBase64) { mimetype += ';base64'; } + } + else { + useBase64 = /;base64$/.test(mimetype); + } + + var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment); + if (!fileSync.contents) { + logger.warn("Skipped data-uri embedding because file not found"); + return fallback(this, filePathNode || mimetypeNode); + } + var buf = fileSync.contents; + + // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded + // and the --ieCompat flag is enabled, return a normal url() instead. + var DATA_URI_MAX_KB = 32, + fileSizeInKB = parseInt((buf.length / 1024), 10); + if (fileSizeInKB >= DATA_URI_MAX_KB) { + + if (this.context.ieCompat !== false) { + logger.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB); + + return fallback(this, filePathNode || mimetypeNode); + } + } + + buf = useBase64 ? buf.toString('base64') + : encodeURIComponent(buf); + + var uri = "\"data:" + mimetype + ',' + buf + fragment + "\""; + return new URL(new Anonymous(uri), this.index, this.currentFileInfo); + }); +}; + +},{"../logger":29,"../tree/anonymous":40,"../tree/url":74,"./function-registry":19}],17:[function(require,module,exports){ +var Keyword = require("../tree/keyword"), + functionRegistry = require("./function-registry"); + +var defaultFunc = { + eval: function () { + var v = this.value_, e = this.error_; + if (e) { + throw e; + } + if (v != null) { + return v ? Keyword.True : Keyword.False; + } + }, + value: function (v) { + this.value_ = v; + }, + error: function (e) { + this.error_ = e; + }, + reset: function () { + this.value_ = this.error_ = null; + } +}; + +functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc)); + +module.exports = defaultFunc; + +},{"../tree/keyword":59,"./function-registry":19}],18:[function(require,module,exports){ +var functionRegistry = require("./function-registry"); + +var functionCaller = function(name, context, index, currentFileInfo) { + this.name = name.toLowerCase(); + this.function = functionRegistry.get(this.name); + this.index = index; + this.context = context; + this.currentFileInfo = currentFileInfo; +}; +functionCaller.prototype.isValid = function() { + return Boolean(this.function); +}; +functionCaller.prototype.call = function(args) { + return this.function.apply(this, args); +}; + +module.exports = functionCaller; + +},{"./function-registry":19}],19:[function(require,module,exports){ +module.exports = { + _data: {}, + add: function(name, func) { + if (this._data.hasOwnProperty(name)) { + //TODO warn + } + this._data[name] = func; + }, + addMultiple: function(functions) { + Object.keys(functions).forEach( + function(name) { + this.add(name, functions[name]); + }.bind(this)); + }, + get: function(name) { + return this._data[name]; + } +}; + +},{}],20:[function(require,module,exports){ +module.exports = function(environment) { + var functions = { + functionRegistry: require("./function-registry"), + functionCaller: require("./function-caller") + }; + + //register functions + require("./default"); + require("./color"); + require("./color-blending"); + require("./data-uri")(environment); + require("./math"); + require("./number"); + require("./string"); + require("./svg")(environment); + require("./types"); + + return functions; +}; + +},{"./color":15,"./color-blending":14,"./data-uri":16,"./default":17,"./function-caller":18,"./function-registry":19,"./math":21,"./number":22,"./string":23,"./svg":24,"./types":25}],21:[function(require,module,exports){ +var Dimension = require("../tree/dimension"), + functionRegistry = require("./function-registry"); + +var mathFunctions = { + // name, unit + ceil: null, + floor: null, + sqrt: null, + abs: null, + tan: "", + sin: "", + cos: "", + atan: "rad", + asin: "rad", + acos: "rad" +}; + +function _math(fn, unit, n) { + if (!(n instanceof Dimension)) { + throw { type: "Argument", message: "argument must be a number" }; + } + if (unit == null) { + unit = n.unit; + } else { + n = n.unify(); + } + return new Dimension(fn(parseFloat(n.value)), unit); +} + +for (var f in mathFunctions) { + if (mathFunctions.hasOwnProperty(f)) { + mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]); + } +} + +mathFunctions.round = function (n, f) { + var fraction = typeof(f) === "undefined" ? 0 : f.value; + return _math(function(num) { return num.toFixed(fraction); }, null, n); +}; + +functionRegistry.addMultiple(mathFunctions); + +},{"../tree/dimension":50,"./function-registry":19}],22:[function(require,module,exports){ +var Dimension = require("../tree/dimension"), + Anonymous = require("../tree/anonymous"), + functionRegistry = require("./function-registry"); + +var minMax = function (isMin, args) { + args = Array.prototype.slice.call(args); + switch(args.length) { + case 0: throw { type: "Argument", message: "one or more arguments required" }; + } + var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone, + order = [], // elems only contains original argument values. + values = {}; // key is the unit.toString() for unified Dimension values, + // value is the index into the order array. + for (i = 0; i < args.length; i++) { + current = args[i]; + if (!(current instanceof Dimension)) { + if(Array.isArray(args[i].value)) { + Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value)); + } + continue; + } + currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new Dimension(current.value, unitClone).unify() : current.unify(); + unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString(); + unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic; + unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone; + j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit]; + if (j === undefined) { + if(unitStatic !== undefined && unit !== unitStatic) { + throw{ type: "Argument", message: "incompatible types" }; + } + values[unit] = order.length; + order.push(current); + continue; + } + referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new Dimension(order[j].value, unitClone).unify() : order[j].unify(); + if ( isMin && currentUnified.value < referenceUnified.value || + !isMin && currentUnified.value > referenceUnified.value) { + order[j] = current; + } + } + if (order.length == 1) { + return order[0]; + } + args = order.map(function (a) { return a.toCSS(this.context); }).join(this.context.compress ? "," : ", "); + return new Anonymous((isMin ? "min" : "max") + "(" + args + ")"); +}; +functionRegistry.addMultiple({ + min: function () { + return minMax(true, arguments); + }, + max: function () { + return minMax(false, arguments); + }, + convert: function (val, unit) { + return val.convertTo(unit.value); + }, + pi: function () { + return new Dimension(Math.PI); + }, + mod: function(a, b) { + return new Dimension(a.value % b.value, a.unit); + }, + pow: function(x, y) { + if (typeof x === "number" && typeof y === "number") { + x = new Dimension(x); + y = new Dimension(y); + } else if (!(x instanceof Dimension) || !(y instanceof Dimension)) { + throw { type: "Argument", message: "arguments must be numbers" }; + } + + return new Dimension(Math.pow(x.value, y.value), x.unit); + }, + percentage: function (n) { + return new Dimension(n.value * 100, '%'); + } +}); + +},{"../tree/anonymous":40,"../tree/dimension":50,"./function-registry":19}],23:[function(require,module,exports){ +var Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + JavaScript = require("../tree/javascript"), + functionRegistry = require("./function-registry"); + +functionRegistry.addMultiple({ + e: function (str) { + return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value); + }, + escape: function (str) { + return new Anonymous(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29")); + }, + replace: function (string, pattern, replacement, flags) { + var result = string.value; + + result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value); + return new Quoted(string.quote || '', result, string.escaped); + }, + '%': function (string /* arg, arg, ...*/) { + var args = Array.prototype.slice.call(arguments, 1), + result = string.value; + + for (var i = 0; i < args.length; i++) { + /*jshint loopfunc:true */ + result = result.replace(/%[sda]/i, function(token) { + var value = token.match(/s/i) ? args[i].value : args[i].toCSS(); + return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value; + }); + } + result = result.replace(/%%/g, '%'); + return new Quoted(string.quote || '', result, string.escaped); + } +}); + +},{"../tree/anonymous":40,"../tree/javascript":57,"../tree/quoted":67,"./function-registry":19}],24:[function(require,module,exports){ +module.exports = function(environment) { + var Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + functionRegistry = require("./function-registry"); + + functionRegistry.add("svg-gradient", function(direction) { + + function throwArgumentDescriptor() { + throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" }; + } + + if (arguments.length < 3) { + throwArgumentDescriptor(); + } + var stops = Array.prototype.slice.call(arguments, 1), + gradientDirectionSvg, + gradientType = "linear", + rectangleDimension = 'x="0" y="0" width="1" height="1"', + useBase64 = true, + renderEnv = {compress: false}, + returner, + directionValue = direction.toCSS(renderEnv), + i, color, position, positionValue, alpha; + + 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 { type: "Argument", message: "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 < stops.length; i+= 1) { + if (stops[i].value) { + color = stops[i].value[0]; + position = stops[i].value[1]; + } else { + color = stops[i]; + position = undefined; + } + + if (!(color instanceof Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof Dimension))) { + throwArgumentDescriptor(); + } + positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%"; + alpha = color.alpha; + returner += ''; + } + returner += '' + + ''; + + if (useBase64) { + try { + returner = environment.encodeBase64(returner); + } catch(e) { + useBase64 = false; + } + } + + returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'"; + return new URL(new Anonymous(returner), this.index, this.currentFileInfo); + }); +}; + +},{"../tree/anonymous":40,"../tree/color":44,"../tree/dimension":50,"../tree/url":74,"./function-registry":19}],25:[function(require,module,exports){ +var Keyword = require("../tree/keyword"), + Dimension = require("../tree/dimension"), + Color = require("../tree/color"), + Quoted = require("../tree/quoted"), + Anonymous = require("../tree/anonymous"), + URL = require("../tree/url"), + Operation = require("../tree/operation"), + functionRegistry = require("./function-registry"); + +var isa = function (n, Type) { + return (n instanceof Type) ? Keyword.True : Keyword.False; + }, + isunit = function (n, unit) { + return (n instanceof Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False; + }; +functionRegistry.addMultiple({ + iscolor: function (n) { + return isa(n, Color); + }, + isnumber: function (n) { + return isa(n, Dimension); + }, + isstring: function (n) { + return isa(n, Quoted); + }, + iskeyword: function (n) { + return isa(n, Keyword); + }, + isurl: function (n) { + return isa(n, URL); + }, + ispixel: function (n) { + return isunit(n, 'px'); + }, + ispercentage: function (n) { + return isunit(n, '%'); + }, + isem: function (n) { + return isunit(n, 'em'); + }, + isunit: isunit, + unit: function (val, unit) { + if(!(val instanceof Dimension)) { + throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") }; + } + if (unit) { + if (unit instanceof Keyword) { + unit = unit.value; + } else { + unit = unit.toCSS(); + } + } else { + unit = ""; + } + return new Dimension(val.value, unit); + }, + "get-unit": function (n) { + return new Anonymous(n.unit); + }, + extract: function(values, index) { + index = index.value - 1; // (1-based index) + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + return Array.isArray(values.value) ? + values.value[index] : Array(values)[index]; + }, + length: function(values) { + var n = Array.isArray(values.value) ? values.value.length : 1; + return new Dimension(n); + } +}); + +},{"../tree/anonymous":40,"../tree/color":44,"../tree/dimension":50,"../tree/keyword":59,"../tree/operation":65,"../tree/quoted":67,"../tree/url":74,"./function-registry":19}],26:[function(require,module,exports){ +var contexts = require("./contexts"), + Parser = require('./parser/parser'); + +module.exports = function(environment) { + + // FileInfo = { + // 'relativeUrls' - option - whether to adjust URL's to be relative + // 'filename' - full resolved filename of current file + // 'rootpath' - path to append to normal URLs for this node + // 'currentDirectory' - path to the current file, absolute + // 'rootFilename' - filename of the base file + // 'entryPath' - absolute path to the entry file + // 'reference' - whether the file should not be output and only output parts that are referenced + + var ImportManager = function(context, rootFileInfo) { + this.rootFilename = rootFileInfo.filename; + this.paths = context.paths || []; // Search paths, when importing + this.contents = {}; // map - filename to contents of all the files + this.contentsIgnoredChars = {}; // map - filename to lines at the begining of each file to ignore + this.mime = context.mime; + this.error = null; + this.context = context; + // Deprecated? Unused outside of here, could be useful. + this.queue = []; // Files which haven't been imported yet + this.files = []; // Holds the imported parse trees. + }; + ImportManager.prototype.push = function (path, currentFileInfo, importOptions, callback) { + var importManager = this; + this.queue.push(path); + + var fileParsedFunc = function (e, root, fullPath) { + importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue + + var importedEqualsRoot = fullPath === importManager.rootFilename; + + importManager.files[fullPath] = root; + + if (e && !importManager.error) { importManager.error = e; } + + callback(e, root, importedEqualsRoot, fullPath); + }; + + var newFileInfo = { + relativeUrls: this.context.relativeUrls, + entryPath: currentFileInfo.entryPath, + rootpath: currentFileInfo.rootpath, + rootFilename: currentFileInfo.rootFilename + }; + + var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.context, environment); + + if (!fileManager) { + fileParsedFunc({ message: "Could not find a file-manager for " + path }); + return; + } + + fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment) + .then(function loadFileCallback(loadedFile) { + var resolvedFilename = loadedFile.filename, + contents = loadedFile.contents; + + // Pass on an updated rootpath if path of imported file is relative and file + // is in a (sub|sup) directory + // + // Examples: + // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/', + // then rootpath should become 'less/module/nav/' + // - If path of imported file is '../mixins.less' and rootpath is 'less/', + // then rootpath should become 'less/../' + newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename); + if(newFileInfo.relativeUrls) { + newFileInfo.rootpath = fileManager.join((importManager.context.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath)); + if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) { + newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath); + } + } + newFileInfo.filename = resolvedFilename; + + var newEnv = new contexts.Parse(importManager.context); + + newEnv.processImports = false; + importManager.contents[resolvedFilename] = contents; + + if (currentFileInfo.reference || importOptions.reference) { + newFileInfo.reference = true; + } + + if (importOptions.inline) { + fileParsedFunc(null, contents, resolvedFilename); + } else { + new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) { + fileParsedFunc(e, root, resolvedFilename); + }); + } + }, + function(error) { + fileParsedFunc(error); + }); + }; + return ImportManager; +}; + +},{"./contexts":8,"./parser/parser":33}],27:[function(require,module,exports){ +module.exports = function(environment, fileManagers) { + var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment; + + var less = { + version: [2, 0, "0-b1"], + data: require('./data'), + tree: require('./tree'), + Environment: (Environment = require("./environment/environment")), + AbstractFileManager: require("./environment/abstract-file-manager"), + environment: (environment = new Environment(environment, fileManagers)), + visitors: require('./visitors'), + Parser: require('./parser/parser'), + functions: require('./functions')(environment), + contexts: require("./contexts"), + SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)), + SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput)), + ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)), + ImportManager: (ImportManager = require('./import-manager')(environment)), + render: require("./render")(environment, ParseTree, ImportManager), + LessError: require('./less-error'), + transformTree: require('./transform-tree'), + utils: require('./utils'), + PluginManager: require('./plugin-manager'), + logger: require('./logger') + }; + + return less; +}; + +},{"./contexts":8,"./data":10,"./environment/abstract-file-manager":12,"./environment/environment":13,"./functions":20,"./import-manager":26,"./less-error":28,"./logger":29,"./parse-tree":30,"./parser/parser":33,"./plugin-manager":34,"./render":35,"./source-map-builder":36,"./source-map-output":37,"./transform-tree":38,"./tree":56,"./utils":77,"./visitors":80}],28:[function(require,module,exports){ +var utils = require("./utils"); + +var LessError = module.exports = function LessError(e, importManager, currentFilename) { + + Error.call(this); + + var filename = e.filename || currentFilename; + + if (importManager && filename) { + var input = importManager.contents[filename], + loc = utils.getLocation(e.index, input), + line = loc.line, + col = loc.column, + callLine = e.call && utils.getLocation(e.call, input).line, + lines = input.split('\n'); + + this.type = e.type || 'Syntax'; + this.filename = filename; + this.index = e.index; + this.line = typeof(line) === 'number' ? line + 1 : null; + this.callLine = callLine + 1; + this.callExtract = lines[callLine]; + this.column = col; + this.extract = [ + lines[line - 1], + lines[line], + lines[line + 1] + ]; + } + this.message = e.message; + this.stack = e.stack; +}; + +LessError.prototype = Object.create(Error.prototype); +LessError.prototype.constructor = LessError; + +},{"./utils":77}],29:[function(require,module,exports){ +module.exports = { + error: function(msg) { + this._fireEvent("error", msg); + }, + warn: function(msg) { + this._fireEvent("warn", msg); + }, + info: function(msg) { + this._fireEvent("info", msg); + }, + debug: function(msg) { + this._fireEvent("debug", msg); + }, + addListener: function(listener) { + this._listeners.push(listener); + }, + removeListener: function(listener) { + for(var i = 0; i < this._listeners.length; i++) { + if (this._listeners[i] === listener) { + this._listeners.splice(i, 1); + return; + } + } + }, + _fireEvent: function(type, msg) { + for(var i = 0; i < this._listeners.length; i++) { + var logFunction = this._listeners[i][type]; + if (logFunction) { + logFunction(msg); + } + } + }, + _listeners: [] +}; + +},{}],30:[function(require,module,exports){ +var LessError = require('./less-error'), + transformTree = require("./transform-tree"); + +module.exports = function(SourceMapBuilder) { +var ParseTree = function(root, imports) { + this.root = root; + this.imports = imports; +}; + +ParseTree.prototype.toCSS = function(options) { + var evaldRoot, result = {}, sourceMapBuilder; + try { + evaldRoot = transformTree(this.root, options); + } catch (e) { + throw new LessError(e, this.imports); + } + + try { + var toCSSOptions = { + compress: Boolean(options.compress), + dumpLineNumbers: options.dumpLineNumbers, + strictUnits: Boolean(options.strictUnits), + numPrecision: 8}; + + if (options.sourceMap) { + sourceMapBuilder = new SourceMapBuilder(options.sourceMap); + result.css = sourceMapBuilder.toCSS(evaldRoot, toCSSOptions, this.imports); + } else { + result.css = evaldRoot.toCSS(toCSSOptions); + } + } catch (e) { + throw new LessError(e, this.imports); + } + + if (options.pluginManager) { + var postProcessors = options.pluginManager.getPostProcessors(); + for(var i = 0; i < postProcessors.length; i++) { + result.css = postProcessors[i].process(result.css, { sourceMap: sourceMapBuilder, options: options, imports: this.imports }); + } + } + if (options.sourceMap) { + result.map = sourceMapBuilder.getExternalSourceMap(); + } + return result; +}; +return ParseTree; +}; + +},{"./less-error":28,"./transform-tree":38}],31:[function(require,module,exports){ +// Split the input into chunks. +module.exports = function (input, fail) { + var len = input.length, level = 0, parenLevel = 0, + lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace, + chunks = [], emitFrom = 0, + chunkerCurrentIndex, currentChunkStartIndex, cc, cc2, matched; + + function emitChunk(force) { + var len = chunkerCurrentIndex - emitFrom; + if (((len < 512) && !force) || !len) { + return; + } + chunks.push(input.slice(emitFrom, chunkerCurrentIndex + 1)); + emitFrom = chunkerCurrentIndex + 1; + } + + for (chunkerCurrentIndex = 0; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc = input.charCodeAt(chunkerCurrentIndex); + if (((cc >= 97) && (cc <= 122)) || (cc < 34)) { + // a-z or whitespace + continue; + } + + switch (cc) { + case 40: // ( + parenLevel++; + lastOpeningParen = chunkerCurrentIndex; + continue; + case 41: // ) + if (--parenLevel < 0) { + return fail("missing opening `(`", chunkerCurrentIndex); + } + continue; + case 59: // ; + if (!parenLevel) { emitChunk(); } + continue; + case 123: // { + level++; + lastOpening = chunkerCurrentIndex; + continue; + case 125: // } + if (--level < 0) { + return fail("missing opening `{`", chunkerCurrentIndex); + } + if (!level && !parenLevel) { emitChunk(); } + continue; + case 92: // \ + if (chunkerCurrentIndex < len - 1) { chunkerCurrentIndex++; continue; } + return fail("unescaped `\\`", chunkerCurrentIndex); + case 34: + case 39: + case 96: // ", ' and ` + matched = 0; + currentChunkStartIndex = chunkerCurrentIndex; + for (chunkerCurrentIndex = chunkerCurrentIndex + 1; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if (cc2 > 96) { continue; } + if (cc2 == cc) { matched = 1; break; } + if (cc2 == 92) { // \ + if (chunkerCurrentIndex == len - 1) { + return fail("unescaped `\\`", chunkerCurrentIndex); + } + chunkerCurrentIndex++; + } + } + if (matched) { continue; } + return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex); + case 47: // /, check for comment + if (parenLevel || (chunkerCurrentIndex == len - 1)) { continue; } + cc2 = input.charCodeAt(chunkerCurrentIndex + 1); + if (cc2 == 47) { + // //, find lnfeed + for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; } + } + } else if (cc2 == 42) { + // /*, find */ + lastMultiComment = currentChunkStartIndex = chunkerCurrentIndex; + for (chunkerCurrentIndex = chunkerCurrentIndex + 2; chunkerCurrentIndex < len - 1; chunkerCurrentIndex++) { + cc2 = input.charCodeAt(chunkerCurrentIndex); + if (cc2 == 125) { lastMultiCommentEndBrace = chunkerCurrentIndex; } + if (cc2 != 42) { continue; } + if (input.charCodeAt(chunkerCurrentIndex + 1) == 47) { break; } + } + if (chunkerCurrentIndex == len - 1) { + return fail("missing closing `*/`", currentChunkStartIndex); + } + chunkerCurrentIndex++; + } + continue; + case 42: // *, check for unmatched */ + if ((chunkerCurrentIndex < len - 1) && (input.charCodeAt(chunkerCurrentIndex + 1) == 47)) { + return fail("unmatched `/*`", chunkerCurrentIndex); + } + continue; + } + } + + if (level !== 0) { + if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) { + return fail("missing closing `}` or `*/`", lastOpening); + } else { + return fail("missing closing `}`", lastOpening); + } + } else if (parenLevel !== 0) { + return fail("missing closing `)`", lastOpeningParen); + } + + emitChunk(true); + return chunks; +}; + +},{}],32:[function(require,module,exports){ +var chunker = require('./chunker'); + +module.exports = function() { + var input, // LeSS input string + j, // current chunk + saveStack = [], // holds state for backtracking + furthest, // furthest index the parser has gone to + furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause + chunks, // chunkified input + current, // current chunk + currentPos, // index of current chunk, in `input` + parserInput = {}; + + parserInput.save = function() { + currentPos = parserInput.i; + saveStack.push( { current: current, i: parserInput.i, j: j }); + }; + parserInput.restore = function(possibleErrorMessage) { + if (parserInput.i > furthest) { + furthest = parserInput.i; + furthestPossibleErrorMessage = possibleErrorMessage; + } + var state = saveStack.pop(); + current = state.current; + currentPos = parserInput.i = state.i; + j = state.j; + }; + parserInput.forget = function() { + saveStack.pop(); + }; + function sync() { + if (parserInput.i > currentPos) { + current = current.slice(parserInput.i - currentPos); + currentPos = parserInput.i; + } + } + parserInput.isWhitespace = function (offset) { + var pos = parserInput.i + (offset || 0), + code = input.charCodeAt(pos); + return (code === CHARCODE_SPACE || code === CHARCODE_CR || code === CHARCODE_TAB || code === CHARCODE_LF); + }; + // + // Parse from a token, regexp or string, and move forward if match + // + parserInput.$ = function(tok) { + var tokType = typeof tok, + match, length; + + // Either match a single character in the input, + // or match a regexp in the current chunk (`current`). + // + if (tokType === "string") { + if (input.charAt(parserInput.i) !== tok) { + return null; + } + skipWhitespace(1); + return tok; + } + + // regexp + sync(); + if (! (match = tok.exec(current))) { + return null; + } + + length = match[0].length; + + // The match is confirmed, add the match length to `i`, + // 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. + // + skipWhitespace(length); + + if(typeof(match) === 'string') { + return match; + } else { + return match.length === 1 ? match[0] : match; + } + }; + + // Specialization of $(tok) + parserInput.$re = function(tok) { + if (parserInput.i > currentPos) { + current = current.slice(parserInput.i - currentPos); + currentPos = parserInput.i; + } + var m = tok.exec(current); + if (!m) { + return null; + } + + skipWhitespace(m[0].length); + if(typeof m === "string") { + return m; + } + + return m.length === 1 ? m[0] : m; + }; + + // Specialization of $(tok) + parserInput.$char = function(tok) { + if (input.charAt(parserInput.i) !== tok) { + return null; + } + skipWhitespace(1); + return tok; + }; + + var CHARCODE_SPACE = 32, + CHARCODE_TAB = 9, + CHARCODE_LF = 10, + CHARCODE_CR = 13, + CHARCODE_PLUS = 43, + CHARCODE_COMMA = 44, + CHARCODE_FORWARD_SLASH = 47, + CHARCODE_9 = 57; + + parserInput.autoCommentAbsorb = true; + parserInput.commentStore = []; + parserInput.finished = false; + + var skipWhitespace = function(length) { + var oldi = parserInput.i, oldj = j, + curr = parserInput.i - currentPos, + endIndex = parserInput.i + current.length - curr, + mem = (parserInput.i += length), + inp = input, + c, nextChar, comment; + + for (; parserInput.i < endIndex; parserInput.i++) { + c = inp.charCodeAt(parserInput.i); + + if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) { + nextChar = inp[parserInput.i + 1]; + if (nextChar === '/') { + comment = {index: parserInput.i, isLineComment: true}; + var nextNewLine = inp.indexOf("\n", parserInput.i + 1); + if (nextNewLine < 0) { + nextNewLine = endIndex; + } + parserInput.i = nextNewLine; + comment.text = inp.substr(comment.i, parserInput.i - comment.i); + parserInput.commentStore.push(comment); + continue; + } else if (nextChar === '*') { + var haystack = inp.substr(parserInput.i); + var comment_search_result = haystack.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//); + if (comment_search_result) { + comment = { + index: parserInput.i, + text: comment_search_result[0], + isLineComment: false + }; + parserInput.i += comment.text.length - 1; + parserInput.commentStore.push(comment); + continue; + } + } + break; + } + + if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) { + break; + } + } + + current = current.slice(length + parserInput.i - mem + curr); + currentPos = parserInput.i; + + if (!current.length) { + if (j < chunks.length - 1) + { + current = chunks[++j]; + skipWhitespace(0); // skip space at the beginning of a chunk + return true; // things changed + } + parserInput.finished = true; + } + + return oldi !== parserInput.i || oldj !== j; + }; + + // Same as $(), but don't change the state of the parser, + // just return the match. + parserInput.peek = function(tok) { + if (typeof(tok) === 'string') { + return input.charAt(parserInput.i) === tok; + } else { + return tok.test(current); + } + }; + + // Specialization of peek() + // TODO remove or change some currentChar calls to peekChar + parserInput.peekChar = function(tok) { + return input.charAt(parserInput.i) === tok; + }; + + parserInput.currentChar = function() { + return input.charAt(parserInput.i); + }; + + parserInput.getInput = function() { + return input; + }; + + parserInput.peekNotNumeric = function() { + var c = input.charCodeAt(parserInput.i); + //Is the first char of the dimension 0-9, '.', '+' or '-' + return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA; + }; + + parserInput.start = function(str, chunkInput, failFunction) { + input = str; + parserInput.i = j = currentPos = furthest = 0; + + // chunking apparantly makes things quicker (but my tests indicate + // it might actually make things slower in node at least) + // and it is a non-perfect parse - it can't recognise + // unquoted urls, meaning it can't distinguish comments + // meaning comments with quotes or {}() in them get 'counted' + // and then lead to parse errors. + // In addition if the chunking chunks in the wrong place we might + // not be able to parse a parser statement in one go + // this is officially deprecated but can be switched on via an option + // in the case it causes too much performance issues. + if (chunkInput) { + chunks = chunker(str, failFunction); + } else { + chunks = [str]; + } + + current = chunks[0]; + + skipWhitespace(0); + }; + + parserInput.end = function() { + var message, + isFinished = parserInput.i >= input.length - 1; + + if (parserInput.i < furthest) { + message = furthestPossibleErrorMessage; + parserInput.i = furthest; + } + return { + isFinished: isFinished, + furthest: parserInput.i, + furthestPossibleErrorMessage: message, + furthestReachedEnd: parserInput.i >= input.length - 1, + furthestChar: input[parserInput.i] + }; + }; + + return parserInput; +}; + +},{"./chunker":31}],33:[function(require,module,exports){ +var LessError = require('../less-error'), + tree = require("../tree"), + visitors = require("../visitors"), + getParserInput = require("./parser-input"), + utils = require("../utils"); + +// +// less.js - parser +// +// A relatively straight-forward predictive parser. +// There is no tokenization/lexing stage, the input is parsed +// in one sweep. +// +// To make the parser fast enough to run in the browser, several +// optimization had to be made: +// +// - Matching and slicing on a huge input is often cause of slowdowns. +// The solution is to chunkify the input into smaller strings. +// The chunks are stored in the `chunks` var, +// `j` holds the current chunk index, and `currentPos` holds +// the index of the current chunk in relation to `input`. +// This gives us an almost 4x speed-up. +// +// - In many cases, we don't need to match individual tokens; +// for example, if a value doesn't hold any variables, operations +// or dynamic references, the parser can effectively 'skip' it, +// treating it as a literal. +// An example would be '1px solid #000' - which evaluates to itself, +// we don't need to know what the individual components are. +// The drawback, of course is that you don't get the benefits of +// syntax-checking on the CSS. This gives us a 50% speed-up in the parser, +// and a smaller speed-up in the code-gen. +// +// +// Token matching is done with the `$` function, which either takes +// a terminal string or regexp, or a non-terminal function to call. +// It also takes care of moving all the indices forwards. +// +// +var Parser = function Parser(context, imports, fileInfo) { + var parsers, + parserInput = getParserInput(); + + function expect(arg, msg, index) { + // some older browsers return typeof 'function' for RegExp + var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$(arg); + if (result) { + return result; + } + error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + parserInput.currentChar() + "'" + : "unexpected token")); + } + + // Specialization of expect() + function expectChar(arg, msg) { + if (parserInput.$char(arg)) { + return arg; + } + error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'"); + } + + function error(msg, type) { + throw new LessError( + { + index: parserInput.i, + filename: fileInfo.filename, + type: type || 'Syntax', + message: msg + }, + imports + ); + } + + function getDebugInfo(index) { + var filename = fileInfo.filename; + + return { + lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1, + fileName: filename + }; + } + + // + // The Parser + // + return { + + // + // Parse an input string into an abstract syntax tree, + // @param str A string containing 'less' markup + // @param callback call `callback` when done. + // @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply + // + parse: function (str, callback, additionalData) { + var root, error = null, globalVars, modifyVars, preText = ""; + + globalVars = (additionalData && additionalData.globalVars) ? Parser.serializeVars(additionalData.globalVars) + '\n' : ''; + modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + Parser.serializeVars(additionalData.modifyVars) : ''; + + if (globalVars || (additionalData && additionalData.banner)) { + preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars; + imports.contentsIgnoredChars[fileInfo.filename] = preText.length; + } + + str = str.replace(/\r\n/g, '\n'); + // Remove potential UTF Byte Order Mark + str = preText + str.replace(/^\uFEFF/, '') + modifyVars; + imports.contents[fileInfo.filename] = str; + + // Start with the primary rule. + // The whole syntax tree is held under a Ruleset node, + // with the `root` property set to true, so no `{}` are + // output. The callback is called when the input is parsed. + try { + parserInput.start(str, context.chunkInput, function fail(msg, index) { + throw LessError({ + index: index, + type: 'Parse', + message: msg, + filename: fileInfo.filename + }, imports); + }); + + root = new(tree.Ruleset)(null, this.parsers.primary()); + root.root = true; + root.firstRoot = true; + } catch (e) { + return callback(new LessError(e, imports, fileInfo.filename)); + } + + // If `i` is smaller than the `input.length - 1`, + // it means the parser wasn't able to parse the whole + // string, so we've got a parsing error. + // + // We try to extract a \n delimited string, + // showing the line where the parse error occurred. + // We split it up into two parts (the part which parsed, + // and the part which didn't), so we can color them differently. + var endInfo = parserInput.end(); + if (!endInfo.isFinished) { + + var message = endInfo.furthestPossibleErrorMessage; + + if (!message) { + message = "Unrecognised input"; + if (endInfo.furthestChar === '}') { + message += ". Possibly missing opening '{'"; + } else if (endInfo.furthestChar === ')') { + message += ". Possibly missing opening '('"; + } else if (endInfo.furthestReachedEnd) { + message += ". Possibly missing something"; + } + } + + error = new LessError({ + type: "Parse", + message: message, + index: endInfo.furthest, + filename: fileInfo.filename + }, imports); + } + + var finish = function (e) { + e = error || e || imports.error; + + if (e) { + if (!(e instanceof LessError)) { + e = new LessError(e, imports, fileInfo.filename); + } + + return callback(e); + } + else { + return callback(null, root); + } + }; + + if (context.processImports !== false) { + new visitors.ImportVisitor(imports, finish) + .run(root); + } else { + return finish(); + } + }, + + // + // 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()`. + // + parsers: parsers = { + // + // 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. + // + primary: function () { + var mixin = this.mixin, root = [], node; + + while (!parserInput.finished) + { + while(true) { + node = this.comment(); + if (!node) { break; } + root.push(node); + } + if (parserInput.peek('}')) { + break; + } + node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() || + mixin.call() || this.rulesetCall() || this.directive(); + if (node) { + root.push(node); + } else { + if (!(parserInput.$re(/^[\s\n]+/) || parserInput.$re(/^;+/))) { + break; + } + } + } + + return root; + }, + + // comments are collected by the main parsing mechanism and then assigned to nodes + // where the current structure allows it + comment: function () { + if (parserInput.commentStore.length) { + var comment = parserInput.commentStore.shift(); + return new(tree.Comment)(comment.text, comment.isLineComment, comment.index, fileInfo); + } + }, + + // + // Entities are tokens which can be found inside an Expression + // + entities: { + // + // A string, which supports escaping " and ' + // + // "milky way" 'he\'s the one!' + // + quoted: function () { + var str, index = parserInput.i; + + str = parserInput.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/); + if (str) { + return new(tree.Quoted)(str[2], str[3] || str[4], Boolean(str[1]), index, fileInfo); + } + }, + + // + // A catch-all word, such as: + // + // black border-collapse + // + keyword: function () { + var k = parserInput.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/); + if (k) { + return tree.Color.fromKeyword(k) || new(tree.Keyword)(k); + } + }, + + // + // 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. + // + call: function () { + var name, nameLC, args, alpha, index = parserInput.i; + + if (parserInput.peek(/^url\(/i)) { + return; + } + + parserInput.save(); + + name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/); + if (!name) { parserInput.forget(); return; } + + name = name[1]; + nameLC = name.toLowerCase(); + + if (nameLC === 'alpha') { + alpha = parsers.alpha(); + if(alpha) { + return alpha; + } + } + + args = this.arguments(); + + if (! parserInput.$char(')')) { + parserInput.restore("Could not parse call arguments or missing ')'"); + return; + } + + parserInput.forget(); + return new(tree.Call)(name, args, index, fileInfo); + }, + arguments: function () { + var args = [], arg; + + while (true) { + arg = this.assignment() || parsers.expression(); + if (!arg) { + break; + } + args.push(arg); + if (! parserInput.$char(',')) { + break; + } + } + return args; + }, + literal: function () { + return this.dimension() || + this.color() || + this.quoted() || + this.unicodeDescriptor(); + }, + + // Assignments are argument entities for calls. + // They are present in ie filter properties as shown below. + // + // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) + // + + assignment: function () { + var key, value; + key = parserInput.$re(/^\w+(?=\s?=)/i); + if (!key) { + return; + } + if (!parserInput.$char('=')) { + return; + } + value = parsers.entity(); + if (value) { + return new(tree.Assignment)(key, 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. + // + url: function () { + var value, index = parserInput.i; + + if (parserInput.currentChar() !== 'u' || !parserInput.$re(/^url\(/)) { + return; + } + + parserInput.autoCommentAbsorb = false; + + value = this.quoted() || this.variable() || + parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || ""; + + parserInput.autoCommentAbsorb = true; + + expectChar(')'); + + return new(tree.URL)((value.value != null || value instanceof tree.Variable) ? + value : new(tree.Anonymous)(value), index, fileInfo); + }, + + // + // A Variable entity, such as `@fink`, in + // + // width: @fink + 2px + // + // We use a different parser for variable definitions, + // see `parsers.variable`. + // + variable: function () { + var name, index = parserInput.i; + + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) { + return new(tree.Variable)(name, index, fileInfo); + } + }, + + // A variable entity useing the protective {} e.g. @{var} + variableCurly: function () { + var curly, index = parserInput.i; + + if (parserInput.currentChar() === '@' && (curly = parserInput.$re(/^@\{([\w-]+)\}/))) { + return new(tree.Variable)("@" + curly[1], index, fileInfo); + } + }, + + // + // A Hexadecimal color + // + // #4F3C2F + // + // `rgb` and `hsl` colors are parsed through the `entities.call` parser. + // + color: function () { + var rgb; + + if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) { + var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string + colorCandidateString = colorCandidateString[1]; + if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters + error("Invalid HEX color code"); + } + return new(tree.Color)(rgb[1]); + } + }, + + // + // A Dimension, that is, a number and a unit + // + // 0.5em 95% + // + dimension: function () { + if (parserInput.peekNotNumeric()) { + return; + } + + var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i); + if (value) { + return new(tree.Dimension)(value[1], value[2]); + } + }, + + // + // A unicode descriptor, as is used in unicode-range + // + // U+0?? or U+00A1-00A9 + // + unicodeDescriptor: function () { + var ud; + + ud = parserInput.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/); + if (ud) { + return new(tree.UnicodeDescriptor)(ud[0]); + } + }, + + // + // JavaScript code to be evaluated + // + // `window.location.href` + // + javascript: function () { + var js, index = parserInput.i; + + js = parserInput.$re(/^(~)?`([^`]*)`/); + if (js) { + return new(tree.JavaScript)(js[2], Boolean(js[1]), index, fileInfo); + } + } + }, + + // + // The variable part of a variable definition. Used in the `rule` parser + // + // @fink: + // + variable: function () { + var name; + + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*:/))) { return name[1]; } + }, + + // + // The variable part of a variable definition. Used in the `rule` parser + // + // @fink(); + // + rulesetCall: function () { + var name; + + if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) { + return new tree.RulesetCall(name[1]); + } + }, + + // + // extend syntax - used to extend selectors + // + extend: function(isRule) { + var elements, e, index = parserInput.i, option, extendList, extend; + + if (!(isRule ? parserInput.$re(/^&:extend\(/) : parserInput.$re(/^:extend\(/))) { return; } + + do { + option = null; + elements = null; + while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) { + e = this.element(); + if (!e) { break; } + if (elements) { elements.push(e); } else { elements = [ e ]; } + } + + option = option && option[1]; + if (!elements) + error("Missing target selector for :extend()."); + extend = new(tree.Extend)(new(tree.Selector)(elements), option, index); + if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } + + } while(parserInput.$char(",")); + + expect(/^\)/); + + if (isRule) { + expect(/^;/); + } + + return extendList; + }, + + // + // extendRule - used in a rule to extend all the parent selectors + // + extendRule: function() { + return this.extend(true); + }, + + // + // Mixins + // + mixin: { + // + // 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. + // + call: function () { + var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex, + elements, elem, e, c, args; + + if (s !== '.' && s !== '#') { return; } + + parserInput.save(); // stop us absorbing part of an invalid selector + + while (true) { + elemIndex = parserInput.i; + e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/); + if (!e) { + break; + } + elem = new(tree.Element)(c, e, elemIndex, fileInfo); + if (elements) { elements.push(elem); } else { elements = [ elem ]; } + c = parserInput.$char('>'); + } + + if (elements) { + if (parserInput.$char('(')) { + args = this.args(true).args; + expectChar(')'); + } + + if (parsers.important()) { + important = true; + } + + if (parsers.end()) { + parserInput.forget(); + return new(tree.mixin.Call)(elements, args, index, fileInfo, important); + } + } + + parserInput.restore(); + }, + args: function (isCall) { + var entities = parsers.entities, + returner = { args:null, variadic: false }, + expressions = [], argsSemiColon = [], argsComma = [], + isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg; + + parserInput.save(); + + while (true) { + if (isCall) { + arg = parsers.detachedRuleset() || parsers.expression(); + } else { + parserInput.commentStore.length = 0; + if (parserInput.currentChar() === '.' && parserInput.$re(/^\.{3}/)) { + returner.variadic = true; + if (parserInput.$char(";") && !isSemiColonSeperated) { + isSemiColonSeperated = true; + } + (isSemiColonSeperated ? argsSemiColon : argsComma) + .push({ variadic: true }); + break; + } + arg = entities.variable() || entities.literal() || entities.keyword(); + } + + if (!arg) { + break; + } + + nameLoop = null; + if (arg.throwAwayComments) { + arg.throwAwayComments(); + } + value = arg; + var val = null; + + if (isCall) { + // Variable + if (arg.value && arg.value.length == 1) { + val = arg.value[0]; + } + } else { + val = arg; + } + + if (val && val instanceof tree.Variable) { + if (parserInput.$char(':')) { + if (expressions.length > 0) { + if (isSemiColonSeperated) { + 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 = (isCall && parsers.detachedRuleset()) || parsers.expression(); + + if (!value) { + if (isCall) { + error("could not understand value for named argument"); + } else { + parserInput.restore(); + returner.args = []; + return returner; + } + } + nameLoop = (name = val.name); + } else if (!isCall && parserInput.$re(/^\.{3}/)) { + returner.variadic = true; + if (parserInput.$char(";") && !isSemiColonSeperated) { + isSemiColonSeperated = true; + } + (isSemiColonSeperated ? argsSemiColon : argsComma) + .push({ name: arg.name, variadic: true }); + break; + } else if (!isCall) { + name = nameLoop = val.name; + value = null; + } + } + + if (value) { + expressions.push(value); + } + + argsComma.push({ name:nameLoop, value:value }); + + if (parserInput.$char(',')) { + continue; + } + + if (parserInput.$char(';') || isSemiColonSeperated) { + + if (expressionContainsNamed) { + error("Cannot mix ; and , as delimiter types"); + } + + isSemiColonSeperated = true; + + if (expressions.length > 1) { + value = new(tree.Value)(expressions); + } + argsSemiColon.push({ name:name, value:value }); + + name = null; + expressions = []; + expressionContainsNamed = false; + } + } + + parserInput.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. + // + definition: function () { + var name, params = [], match, ruleset, cond, variadic = false; + if ((parserInput.currentChar() !== '.' && parserInput.currentChar() !== '#') || + parserInput.peek(/^[^{]*\}/)) { + return; + } + + parserInput.save(); + + match = parserInput.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/); + if (match) { + name = match[1]; + + var argInfo = this.args(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 (!parserInput.$char(')')) { + parserInput.restore("Missing closing ')'"); + return; + } + + parserInput.commentStore.length = 0; + + if (parserInput.$re(/^when/)) { // Guard + cond = expect(parsers.conditions, 'expected condition'); + } + + ruleset = parsers.block(); + + if (ruleset) { + parserInput.forget(); + return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic); + } else { + parserInput.restore(); + } + } else { + parserInput.forget(); + } + } + }, + + // + // Entities are the smallest recognized token, + // and can be found inside a rule's value. + // + entity: function () { + var entities = this.entities; + + return this.comment() || entities.literal() || entities.variable() || entities.url() || + entities.call() || entities.keyword() || entities.javascript(); + }, + + // + // 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. + // + end: function () { + return parserInput.$char(';') || parserInput.peek('}'); + }, + + // + // IE's alpha function + // + // alpha(opacity=88) + // + alpha: function () { + var value; + + if (! parserInput.$re(/^opacity=/i)) { return; } + value = parserInput.$re(/^\d+/); + if (!value) { + value = expect(this.entities.variable, "Could not parse alpha"); + } + expectChar(')'); + return new(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 `*`. + // + element: function () { + var e, c, v, index = parserInput.i; + + c = this.combinator(); + + e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) || + parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^()@]+\)/) || parserInput.$re(/^[\.#](?=@)/) || + this.entities.variableCurly(); + + if (! e) { + parserInput.save(); + if (parserInput.$char('(')) { + if ((v = this.selector()) && parserInput.$char(')')) { + e = new(tree.Paren)(v); + parserInput.forget(); + } else { + parserInput.restore("Missing closing ')'"); + } + } else { + parserInput.forget(); + } + } + + if (e) { return new(tree.Element)(c, e, index, fileInfo); } + }, + + // + // 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. More info on how + // we deal with this in *combinator.js*. + // + combinator: function () { + var c = parserInput.currentChar(); + + if (c === '/') { + parserInput.save(); + var slashedCombinator = parserInput.$re(/^\/[a-z]+\//i); + if (slashedCombinator) { + parserInput.forget(); + return new(tree.Combinator)(slashedCombinator); + } + parserInput.restore(); + } + + if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') { + parserInput.i++; + if (c === '^' && parserInput.currentChar() === '^') { + c = '^^'; + parserInput.i++; + } + while (parserInput.isWhitespace()) { parserInput.i++; } + return new(tree.Combinator)(c); + } else if (parserInput.isWhitespace(-1)) { + return new(tree.Combinator)(" "); + } else { + return new(tree.Combinator)(null); + } + }, + // + // A CSS selector (see selector below) + // with less extensions e.g. the ability to extend and guard + // + lessSelector: function () { + return this.selector(true); + }, + // + // A CSS Selector + // + // .class > div + h1 + // li a:hover + // + // Selectors are made out of one or more Elements, see above. + // + selector: function (isLess) { + var index = parserInput.i, elements, extendList, c, e, extend, when, condition; + + while ((isLess && (extend = this.extend())) || (isLess && (when = parserInput.$re(/^when/))) || (e = this.element())) { + if (when) { + condition = expect(this.conditions, 'expected condition'); + } else if (condition) { + error("CSS guard can only be used at the end of selector"); + } else if (extend) { + if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; } + } else { + if (extendList) { error("Extend can only be used at the end of selector"); } + c = parserInput.currentChar(); + if (elements) { elements.push(e); } else { elements = [ e ]; } + e = null; + } + if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') { + break; + } + } + + if (elements) { return new(tree.Selector)(elements, extendList, condition, index, fileInfo); } + if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); } + }, + attribute: function () { + if (! parserInput.$char('[')) { return; } + + var entities = this.entities, + key, val, op; + + if (!(key = entities.variableCurly())) { + key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/); + } + + op = parserInput.$re(/^[|~*$^]?=/); + if (op) { + val = entities.quoted() || parserInput.$re(/^[0-9]+%/) || parserInput.$re(/^[\w-]+/) || entities.variableCurly(); + } + + expectChar(']'); + + return new(tree.Attribute)(key, op, val); + }, + + // + // The `block` rule is used by `ruleset` and `mixin.definition`. + // It's a wrapper around the `primary` rule, with added `{}`. + // + block: function () { + var content; + if (parserInput.$char('{') && (content = this.primary()) && parserInput.$char('}')) { + return content; + } + }, + + blockRuleset: function() { + var block = this.block(); + + if (block) { + block = new tree.Ruleset(null, block); + } + return block; + }, + + detachedRuleset: function() { + var blockRuleset = this.blockRuleset(); + if (blockRuleset) { + return new tree.DetachedRuleset(blockRuleset); + } + }, + + // + // div, .class, body > p {...} + // + ruleset: function () { + var selectors, s, rules, debugInfo; + + parserInput.save(); + + if (context.dumpLineNumbers) { + debugInfo = getDebugInfo(parserInput.i); + } + + while (true) { + s = this.lessSelector(); + if (!s) { + break; + } + if (selectors) { selectors.push(s); } else { selectors = [ s ]; } + parserInput.commentStore.length = 0; + if (s.condition && selectors.length > 1) { + error("Guards are only currently allowed on a single selector."); + } + if (! parserInput.$char(',')) { break; } + if (s.condition) { + error("Guards are only currently allowed on a single selector."); + } + parserInput.commentStore.length = 0; + } + + if (selectors && (rules = this.block())) { + parserInput.forget(); + var ruleset = new(tree.Ruleset)(selectors, rules, context.strictImports); + if (context.dumpLineNumbers) { + ruleset.debugInfo = debugInfo; + } + return ruleset; + } else { + parserInput.restore(); + } + }, + rule: function (tryAnonymous) { + var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable; + + if (c === '.' || c === '#' || c === '&') { return; } + + parserInput.save(); + + name = this.variable() || this.ruleProperty(); + if (name) { + isVariable = typeof name === "string"; + + if (isVariable) { + value = this.detachedRuleset(); + } + + parserInput.commentStore.length = 0; + if (!value) { + // 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 + merge = !isVariable && name.pop().value; + + // prefer to try to parse first if its a variable or we are compressing + // but always fallback on the other one + var tryValueFirst = !tryAnonymous && (context.compress || isVariable); + + if (tryValueFirst) { + value = this.value(); + } + if (!value) { + value = this.anonymousValue(); + if (value) { + parserInput.forget(); + // anonymous values absorb the end ';' which is reequired for them to work + return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo); + } + } + if (!tryValueFirst && !value) { + value = this.value(); + } + + important = this.important(); + } + + if (value && this.end()) { + parserInput.forget(); + return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo); + } else { + parserInput.restore(); + if (value && !tryAnonymous) { + return this.rule(true); + } + } + } else { + parserInput.forget(); + } + }, + anonymousValue: function () { + var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/); + if (match) { + return new(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. + // + "import": function () { + var path, features, index = parserInput.i; + + var dir = parserInput.$re(/^@import?\s+/); + + if (dir) { + var options = (dir ? this.importOptions() : null) || {}; + + if ((path = this.entities.quoted() || this.entities.url())) { + features = this.mediaFeatures(); + + if (!parserInput.$(';')) { + parserInput.i = index; + error("missing semi-colon or unrecognised media features on import"); + } + features = features && new(tree.Value)(features); + return new(tree.Import)(path, features, options, index, fileInfo); + } + else + { + parserInput.i = index; + error("malformed import statement"); + } + } + }, + + importOptions: function() { + var o, options = {}, optionName, value; + + // list of options, surrounded by parens + if (! parserInput.$char('(')) { return null; } + do { + o = this.importOption(); + if (o) { + optionName = o; + value = true; + switch(optionName) { + case "css": + optionName = "less"; + value = false; + break; + case "once": + optionName = "multiple"; + value = false; + break; + } + options[optionName] = value; + if (! parserInput.$char(',')) { break; } + } + } while (o); + expectChar(')'); + return options; + }, + + importOption: function() { + var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference)/); + if (opt) { + return opt[1]; + } + }, + + mediaFeature: function () { + var entities = this.entities, nodes = [], e, p; + parserInput.save(); + do { + e = entities.keyword() || entities.variable(); + if (e) { + nodes.push(e); + } else if (parserInput.$char('(')) { + p = this.property(); + e = this.value(); + if (parserInput.$char(')')) { + if (p && e) { + nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true))); + } else if (e) { + nodes.push(new(tree.Paren)(e)); + } else { + parserInput.restore("badly formed media feature definition"); + return null; + } + } else { + parserInput.restore("Missing closing ')'"); + return null; + } + } + } while (e); + + parserInput.forget(); + if (nodes.length > 0) { + return new(tree.Expression)(nodes); + } + }, + + mediaFeatures: function () { + var entities = this.entities, features = [], e; + do { + e = this.mediaFeature(); + if (e) { + features.push(e); + if (! parserInput.$char(',')) { break; } + } else { + e = entities.variable(); + if (e) { + features.push(e); + if (! parserInput.$char(',')) { break; } + } + } + } while (e); + + return features.length > 0 ? features : null; + }, + + media: function () { + var features, rules, media, debugInfo; + + if (context.dumpLineNumbers) { + debugInfo = getDebugInfo(parserInput.i); + } + + if (parserInput.$re(/^@media/)) { + features = this.mediaFeatures(); + + rules = this.block(); + if (rules) { + media = new(tree.Media)(rules, features, parserInput.i, fileInfo); + if (context.dumpLineNumbers) { + media.debugInfo = debugInfo; + } + return media; + } + } + }, + + // + // A CSS Directive + // + // @charset "utf-8"; + // + directive: function () { + var index = parserInput.i, name, value, rules, nonVendorSpecificName, + hasIdentifier, hasExpression, hasUnknown, hasBlock = true; + + if (parserInput.currentChar() !== '@') { return; } + + value = this['import']() || this.media(); + if (value) { + return value; + } + + parserInput.save(); + + name = parserInput.$re(/^@[a-z-]+/); + + if (!name) { return; } + + nonVendorSpecificName = name; + if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) { + nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 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; + } + + parserInput.commentStore.length = 0; + + if (hasIdentifier) { + value = this.entity(); + if (!value) { + error("expected " + name + " identifier"); + } + } else if (hasExpression) { + value = this.expression(); + if (!value) { + error("expected " + name + " expression"); + } + } else if (hasUnknown) { + value = (parserInput.$re(/^[^{;]+/) || '').trim(); + if (value) { + value = new(tree.Anonymous)(value); + } + } + + if (hasBlock) { + rules = this.blockRuleset(); + } + + if (rules || (!hasBlock && value && parserInput.$char(';'))) { + parserInput.forget(); + return new(tree.Directive)(name, value, rules, index, fileInfo, + context.dumpLineNumbers ? getDebugInfo(index) : null); + } + + parserInput.restore("directive options not recognised"); + }, + + // + // 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 `;`. + // + value: function () { + var e, expressions = []; + + do { + e = this.expression(); + if (e) { + expressions.push(e); + if (! parserInput.$char(',')) { break; } + } + } while(e); + + if (expressions.length > 0) { + return new(tree.Value)(expressions); + } + }, + important: function () { + if (parserInput.currentChar() === '!') { + return parserInput.$re(/^! *important/); + } + }, + sub: function () { + var a, e; + + if (parserInput.$char('(')) { + a = this.addition(); + if (a) { + e = new(tree.Expression)([a]); + expectChar(')'); + e.parens = true; + return e; + } + } + }, + multiplication: function () { + var m, a, op, operation, isSpaced; + m = this.operand(); + if (m) { + isSpaced = parserInput.isWhitespace(-1); + while (true) { + if (parserInput.peek(/^\/[*\/]/)) { + break; + } + + parserInput.save(); + + op = parserInput.$char('/') || parserInput.$char('*'); + + if (!op) { parserInput.forget(); break; } + + a = this.operand(); + + if (!a) { parserInput.restore(); break; } + parserInput.forget(); + + m.parensInOp = true; + a.parensInOp = true; + operation = new(tree.Operation)(op, [operation || m, a], isSpaced); + isSpaced = parserInput.isWhitespace(-1); + } + return operation || m; + } + }, + addition: function () { + var m, a, op, operation, isSpaced; + m = this.multiplication(); + if (m) { + isSpaced = parserInput.isWhitespace(-1); + while (true) { + op = parserInput.$re(/^[-+]\s+/) || (!isSpaced && (parserInput.$char('+') || parserInput.$char('-'))); + if (!op) { + break; + } + a = this.multiplication(); + if (!a) { + break; + } + + m.parensInOp = true; + a.parensInOp = true; + operation = new(tree.Operation)(op, [operation || m, a], isSpaced); + isSpaced = parserInput.isWhitespace(-1); + } + return operation || m; + } + }, + conditions: function () { + var a, b, index = parserInput.i, condition; + + a = this.condition(); + if (a) { + while (true) { + if (!parserInput.peek(/^,\s*(not\s*)?\(/) || !parserInput.$char(',')) { + break; + } + b = this.condition(); + if (!b) { + break; + } + condition = new(tree.Condition)('or', condition || a, b, index); + } + return condition || a; + } + }, + condition: function () { + var entities = this.entities, index = parserInput.i, negate = false, + a, b, c, op; + + if (parserInput.$re(/^not/)) { negate = true; } + expectChar('('); + a = this.addition() || entities.keyword() || entities.quoted(); + if (a) { + op = parserInput.$re(/^(?:>=|<=|=<|[<=>])/); + if (op) { + b = this.addition() || entities.keyword() || entities.quoted(); + if (b) { + c = new(tree.Condition)(op, a, b, index, negate); + } else { + error('expected expression'); + } + } else { + c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate); + } + expectChar(')'); + return parserInput.$re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c; + } + }, + + // + // An operand is anything that can be part of an operation, + // such as a Color, or a Variable + // + operand: function () { + var entities = this.entities, negate; + + if (parserInput.peek(/^-[@\(]/)) { + negate = parserInput.$char('-'); + } + + var o = this.sub() || entities.dimension() || + entities.color() || entities.variable() || + entities.call(); + + if (negate) { + o.parensInOp = true; + o = new(tree.Negative)(o); + } + + return o; + }, + + // + // Expressions either represent mathematical operations, + // or white-space delimited Entities. + // + // 1px solid black + // @var * 2 + // + expression: function () { + var entities = [], e, delim; + + do { + e = this.comment(); + if (e) { + entities.push(e); + continue; + } + e = this.addition() || this.entity(); + if (e) { + entities.push(e); + // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here + if (!parserInput.peek(/^\/[\/*]/)) { + delim = parserInput.$char('/'); + if (delim) { + entities.push(new(tree.Anonymous)(delim)); + } + } + } + } while (e); + if (entities.length > 0) { + return new(tree.Expression)(entities); + } + }, + property: function () { + var name = parserInput.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/); + if (name) { + return name[1]; + } + }, + ruleProperty: function () { + var name = [], index = [], s, k; + + parserInput.save(); + + function match(re) { + var i = parserInput.i, + chunk = parserInput.$re(re); + if (chunk) { + index.push(i); + return name.push(chunk[1]); + } + } + + match(/^(\*?)/); + while (true) { + if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) { + break; + } + } + + if ((name.length > 1) && match(/^((?:\+_|\+)?)\s*:/)) { + parserInput.forget(); + + // at last, we have the complete match now. move forward, + // convert name particles to tree objects and return: + if (name[0] === '') { + name.shift(); + index.shift(); + } + for (k = 0; k < name.length; k++) { + s = name[k]; + name[k] = (s.charAt(0) !== '@') ? + new(tree.Keyword)(s) : + new(tree.Variable)('@' + s.slice(2, -1), + index[k], fileInfo); + } + return name; + } + parserInput.restore(); + } + } + }; +}; +Parser.serializeVars = function(vars) { + var s = ''; + + for (var name in vars) { + if (Object.hasOwnProperty.call(vars, name)) { + var value = vars[name]; + s += ((name[0] === '@') ? '' : '@') + name +': '+ value + + ((('' + value).slice(-1) === ';') ? '' : ';'); + } + } + + return s; +}; + +module.exports = Parser; + +},{"../less-error":28,"../tree":56,"../utils":77,"../visitors":80,"./parser-input":32}],34:[function(require,module,exports){ +/** + * Plugin Manager + */ +var PluginManager = function(less) { + this.less = less; + this.visitors = []; + this.postProcessors = []; + this.installedPlugins = []; + this.fileManagers = []; +}; +/** + * Adds all the plugins in the array + * @param {Array} plugins + */ +PluginManager.prototype.addPlugins = function(plugins) { + if (plugins) { + for(var i = 0;i < plugins.length; i++) { + this.addPlugin(plugins[i]); + } + } +}; +/** + * + * @param plugin + */ +PluginManager.prototype.addPlugin = function(plugin) { + this.installedPlugins.push(plugin); + plugin.install(this.less, this); +}; +/** + * Adds a visitor. The visitor object has options on itself to determine + * when it should run. + * @param visitor + */ +PluginManager.prototype.addVisitor = function(visitor) { + this.visitors.push(visitor); +}; +/** + * Adds a post processor object + * @param {object} postProcessor + * @param {number} priority - guidelines 1 = before compression, 1000 = compression, 2000 = after compression + */ +PluginManager.prototype.addPostProcessor = function(postProcessor, priority) { + var indexToInsertAt; + for(indexToInsertAt = 0; indexToInsertAt < this.postProcessors.length; indexToInsertAt++) { + if (this.postProcessors[indexToInsertAt].priority >= priority) { + break; + } + } + this.postProcessors.splice(indexToInsertAt, 0, {postProcessor: postProcessor, priority: priority}); +}; +/** + * + * @param manager + */ +PluginManager.prototype.addFileManager = function(manager) { + this.fileManagers.push(manager); +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getPostProcessors = function() { + var postProcessors = []; + for(var i = 0; i < this.postProcessors.length; i++) { + postProcessors.push(this.postProcessors[i].postProcessor); + } + return postProcessors; +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getVisitors = function() { + return this.visitors; +}; +/** + * + * @returns {Array} + * @private + */ +PluginManager.prototype.getFileManagers = function() { + return this.fileManagers; +}; +module.exports = PluginManager; + +},{}],35:[function(require,module,exports){ +var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, + contexts = require("./contexts"), + Parser = require('./parser/parser'), + PluginManager = require('./plugin-manager'); + +module.exports = function(environment, ParseTree, ImportManager) { + var render = function (input, options, callback) { + options = options || {}; + + if (typeof(options) === 'function') { + callback = options; + options = {}; + } + + if (callback) { + render.call(this, input, options) + .then(function(css) { + callback(null, css); + }, + function(error) { + callback(error); + }); + } else { + var context, + rootFileInfo, + pluginManager = new PluginManager(this); + + pluginManager.addPlugins(options.plugins); + options.pluginManager = pluginManager; + + context = new contexts.Parse(options); + + if (options.rootFileInfo) { + rootFileInfo = options.rootFileInfo; + } else { + var filename = options.filename || "input"; + var entryPath = filename.replace(/[^\/\\]*$/, ""); + rootFileInfo = { + filename: filename, + relativeUrls: context.relativeUrls, + rootpath: context.rootpath || "", + currentDirectory: entryPath, + entryPath: entryPath, + rootFilename: filename + }; + } + + var imports = new ImportManager(context, rootFileInfo); + var parser = new Parser(context, imports, rootFileInfo); + + return new PromiseConstructor(function (resolve, reject) { + parser.parse(input, function (e, root) { + if (e) { return reject(e); } + try { + var parseTree = new ParseTree(root, imports); + var result = parseTree.toCSS(options); + resolve(result); + } + catch (err) { reject( err); } + }, options); + }); + } + }; + return render; +}; + +},{"./contexts":8,"./parser/parser":33,"./plugin-manager":34,"promise":undefined}],36:[function(require,module,exports){ +module.exports = function (SourceMapOutput) { + + var SourceMapBuilder = function (options) { + this.options = options; + }; + + SourceMapBuilder.prototype.toCSS = function(rootNode, options, imports) { + var sourceMapOutput = new SourceMapOutput( + { + contentsIgnoredCharsMap: imports.contentsIgnoredChars, + rootNode: rootNode, + contentsMap: imports.contents, + sourceMapFilename: this.options.sourceMapFilename, + sourceMapURL: this.options.sourceMapURL, + outputFilename: this.options.sourceMapOutputFilename, + sourceMapBasepath: this.options.sourceMapBasepath, + sourceMapRootpath: this.options.sourceMapRootpath, + outputSourceFiles: this.options.outputSourceFiles, + sourceMapGenerator: this.options.sourceMapGenerator, + sourceMapFileInline: this.options.sourceMapFileInline + }); + + var css = sourceMapOutput.toCSS(options); + this.sourceMap = sourceMapOutput.sourceMap; + this.sourceMapURL = sourceMapOutput.sourceMapURL; + if (this.options.sourceMapInputFilename) { + this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename); + } + return css; + }; + + SourceMapBuilder.prototype.getExternalSourceMap = function() { + return this.sourceMap; + }; + SourceMapBuilder.prototype.setExternalSourceMap = function(sourceMap) { + this.sourceMap = sourceMap; + }; + + SourceMapBuilder.prototype.isInline = function() { + return this.options.sourceMapFileInline; + }; + SourceMapBuilder.prototype.getSourceMapURL = function() { + return this.sourceMapURL; + }; + SourceMapBuilder.prototype.getOutputFilename = function() { + return this.options.sourceMapOutputFilename; + }; + SourceMapBuilder.prototype.getInputFilename = function() { + return this.sourceMapInputFilename; + }; + + return SourceMapBuilder; +}; + +},{}],37:[function(require,module,exports){ +module.exports = function (environment) { + + var SourceMapOutput = function (options) { + this._css = []; + this._rootNode = options.rootNode; + this._contentsMap = options.contentsMap; + this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap; + if (options.sourceMapFilename) { + this._sourceMapFilename = options.sourceMapFilename.replace(/\\/g, '/'); + } + this._outputFilename = options.outputFilename; + this.sourceMapURL = options.sourceMapURL; + if (options.sourceMapBasepath) { + this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/'); + } + if (options.sourceMapRootpath) { + this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/'); + if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') { + this._sourceMapRootpath += '/'; + } + } else { + this._sourceMapRootpath = ""; + } + this._outputSourceFiles = options.outputSourceFiles; + this._sourceMapGeneratorConstructor = environment.getSourceMapGenerator(); + this._sourceMapFileInline = options.sourceMapFileInline; + + this._lineNumber = 0; + this._column = 0; + }; + + SourceMapOutput.prototype.normalizeFilename = function(filename) { + filename = filename.replace(/\\/g, '/'); + + if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) { + filename = filename.substring(this._sourceMapBasepath.length); + if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') { + filename = filename.substring(1); + } + } + return (this._sourceMapRootpath || "") + filename; + }; + + SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) { + + //ignore adding empty strings + if (!chunk) { + return; + } + + var lines, + sourceLines, + columns, + sourceColumns, + i; + + if (fileInfo) { + var inputSource = this._contentsMap[fileInfo.filename]; + + // remove vars/banner added to the top of the file + if (this._contentsIgnoredCharsMap[fileInfo.filename]) { + // adjust the index + index -= this._contentsIgnoredCharsMap[fileInfo.filename]; + if (index < 0) { index = 0; } + // adjust the source + inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]); + } + inputSource = inputSource.substring(0, index); + sourceLines = inputSource.split("\n"); + sourceColumns = sourceLines[sourceLines.length-1]; + } + + lines = chunk.split("\n"); + columns = lines[lines.length-1]; + + if (fileInfo) { + if (!mapLines) { + this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column}, + original: { line: sourceLines.length, column: sourceColumns.length}, + source: this.normalizeFilename(fileInfo.filename)}); + } else { + for(i = 0; i < lines.length; i++) { + this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0}, + original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0}, + source: this.normalizeFilename(fileInfo.filename)}); + } + } + } + + if (lines.length === 1) { + this._column += columns.length; + } else { + this._lineNumber += lines.length - 1; + this._column = columns.length; + } + + this._css.push(chunk); + }; + + SourceMapOutput.prototype.isEmpty = function() { + return this._css.length === 0; + }; + + SourceMapOutput.prototype.toCSS = function(context) { + this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null }); + + if (this._outputSourceFiles) { + for(var filename in this._contentsMap) { + if (this._contentsMap.hasOwnProperty(filename)) + { + var source = this._contentsMap[filename]; + if (this._contentsIgnoredCharsMap[filename]) { + source = source.slice(this._contentsIgnoredCharsMap[filename]); + } + this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source); + } + } + } + + this._rootNode.genCSS(context, this); + + if (this._css.length > 0) { + var sourceMapURL, + sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON()); + + if (this.sourceMapURL) { + sourceMapURL = this.sourceMapURL; + } else if (this._sourceMapFilename) { + sourceMapURL = this._sourceMapFilename; + } + this.sourceMapURL = sourceMapURL; + + if (!this._sourceMapFileInline) { + this.sourceMap = sourceMapContent; + } else { + sourceMapURL = "data:application/json;base64," + environment.encodeBase64(sourceMapContent); + } + + if (sourceMapURL) { + this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */"); + } + } + + return this._css.join(''); + }; + + return SourceMapOutput; +}; + +},{}],38:[function(require,module,exports){ +var contexts = require("./contexts"), + visitor = require("./visitors"), + tree = require("./tree"); + +module.exports = function(root, options) { + options = options || {}; + var evaldRoot, + variables = options.variables, + evalEnv = new contexts.Eval(options); + + // + // Allows setting variables with a hash, so: + // + // `{ color: new tree.Color('#f01') }` will become: + // + // new tree.Rule('@color', + // new tree.Value([ + // new tree.Expression([ + // new tree.Color('#f01') + // ]) + // ]) + // ) + // + if (typeof(variables) === 'object' && !Array.isArray(variables)) { + variables = Object.keys(variables).map(function (k) { + var value = variables[k]; + + if (! (value instanceof tree.Value)) { + if (! (value instanceof tree.Expression)) { + value = new tree.Expression([value]); + } + value = new tree.Value([value]); + } + return new tree.Rule('@' + k, value, false, null, 0); + }); + evalEnv.frames = [new tree.Ruleset(null, variables)]; + } + + var preEvalVisitors = [], + visitors = [ + new visitor.JoinSelectorVisitor(), + new visitor.ExtendVisitor(), + new visitor.ToCSSVisitor({compress: Boolean(options.compress)}) + ], i; + + if (options.pluginManager) { + var pluginVisitors = options.pluginManager.getVisitors(); + for(i =0; i < pluginVisitors.length; i++) { + var pluginVisitor = pluginVisitors[i]; + if (pluginVisitor.isPreEvalVisitor) { + preEvalVisitors.push(pluginVisitor); + } else { + if (pluginVisitor.isPreVisitor) { + visitors.splice(0, 0, pluginVisitor); + } else { + visitors.push(pluginVisitor); + } + } + } + } + + for(i = 0; i < preEvalVisitors.length; i++) { + preEvalVisitors[i].run(root); + } + + evaldRoot = root.eval(evalEnv); + + for(i = 0; i < visitors.length; i++) { + visitors[i].run(evaldRoot); + } + + return evaldRoot; +}; + +},{"./contexts":8,"./tree":56,"./visitors":80}],39:[function(require,module,exports){ +var Node = require("./node"); + +var Alpha = function (val) { + this.value = val; +}; +Alpha.prototype = new Node(); +Alpha.prototype.type = "Alpha"; + +Alpha.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Alpha.prototype.eval = function (context) { + if (this.value.eval) { return new Alpha(this.value.eval(context)); } + return this; +}; +Alpha.prototype.genCSS = function (context, output) { + output.add("alpha(opacity="); + + if (this.value.genCSS) { + this.value.genCSS(context, output); + } else { + output.add(this.value); + } + + output.add(")"); +}; + +module.exports = Alpha; + +},{"./node":64}],40:[function(require,module,exports){ +var Node = require("./node"); + +var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) { + this.value = value; + this.index = index; + this.mapLines = mapLines; + this.currentFileInfo = currentFileInfo; + this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike; +}; +Anonymous.prototype = new Node(); +Anonymous.prototype.type = "Anonymous"; +Anonymous.prototype.eval = function () { + return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); +}; +Anonymous.prototype.compare = function (other) { + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; +}; +Anonymous.prototype.isRulesetLike = function() { + return this.rulesetLike; +}; +Anonymous.prototype.genCSS = function (context, output) { + output.add(this.value, this.currentFileInfo, this.index, this.mapLines); +}; +module.exports = Anonymous; + +},{"./node":64}],41:[function(require,module,exports){ +var Node = require("./node"); + +var Assignment = function (key, val) { + this.key = key; + this.value = val; +}; + +Assignment.prototype = new Node(); +Assignment.prototype.type = "Assignment"; +Assignment.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Assignment.prototype.eval = function (context) { + if (this.value.eval) { + return new Assignment(this.key, this.value.eval(context)); + } + return this; +}; +Assignment.prototype.genCSS = function (context, output) { + output.add(this.key + '='); + if (this.value.genCSS) { + this.value.genCSS(context, output); + } else { + output.add(this.value); + } +}; +module.exports = Assignment; + + +},{"./node":64}],42:[function(require,module,exports){ +var Node = require("./node"); + +var Attribute = function (key, op, value) { + this.key = key; + this.op = op; + this.value = value; +}; +Attribute.prototype = new Node(); +Attribute.prototype.type = "Attribute"; +Attribute.prototype.eval = function (context) { + return new Attribute(this.key.eval ? this.key.eval(context) : this.key, + this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value); +}; +Attribute.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context)); +}; +Attribute.prototype.toCSS = function (context) { + var value = this.key.toCSS ? this.key.toCSS(context) : this.key; + + if (this.op) { + value += this.op; + value += (this.value.toCSS ? this.value.toCSS(context) : this.value); + } + + return '[' + value + ']'; +}; +module.exports = Attribute; + +},{"./node":64}],43:[function(require,module,exports){ +var Node = require("./node"), + FunctionCaller = require("../functions/function-caller"); +// +// A function call node. +// +var Call = function (name, args, index, currentFileInfo) { + this.name = name; + this.args = args; + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +Call.prototype = new Node(); +Call.prototype.type = "Call"; +Call.prototype.accept = function (visitor) { + if (this.args) { + this.args = visitor.visitArray(this.args); + } +}; +// +// When evaluating a function call, +// we either find the function in `less.functions` [1], +// in which case we call it, passing the evaluated arguments, +// if this returns null or we cannot find the function, 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. +// +Call.prototype.eval = function (context) { + var args = this.args.map(function (a) { return a.eval(context); }), + result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo); + + if (funcCaller.isValid()) { // 1. + try { + result = funcCaller.call(args); + if (result != null) { + return result; + } + } catch (e) { + throw { type: e.type || "Runtime", + message: "error evaluating function `" + this.name + "`" + + (e.message ? ': ' + e.message : ''), + index: this.index, filename: this.currentFileInfo.filename }; + } + } + + return new Call(this.name, args, this.index, this.currentFileInfo); +}; +Call.prototype.genCSS = function (context, output) { + output.add(this.name + "(", this.currentFileInfo, this.index); + + for(var i = 0; i < this.args.length; i++) { + this.args[i].genCSS(context, output); + if (i + 1 < this.args.length) { + output.add(", "); + } + } + + output.add(")"); +}; +module.exports = Call; + +},{"../functions/function-caller":18,"./node":64}],44:[function(require,module,exports){ +var Node = require("./node"), + colors = require("../data/colors"); + +// +// RGB Colors - #ff0014, #eee +// +var Color = function (rgb, a) { + // + // The end goal here, is to parse the arguments + // into an integer triplet, such as `128, 255, 0` + // + // This facilitates operations and conversions. + // + if (Array.isArray(rgb)) { + this.rgb = rgb; + } else if (rgb.length == 6) { + this.rgb = rgb.match(/.{2}/g).map(function (c) { + return parseInt(c, 16); + }); + } else { + this.rgb = rgb.split('').map(function (c) { + return parseInt(c + c, 16); + }); + } + this.alpha = typeof(a) === 'number' ? a : 1; +}; + +Color.prototype = new Node(); +Color.prototype.type = "Color"; + +function clamp(v, max) { + return Math.min(Math.max(v, 0), max); +} + +function toHex(v) { + return '#' + v.map(function (c) { + c = clamp(Math.round(c), 255); + return (c < 16 ? '0' : '') + c.toString(16); + }).join(''); +} + +Color.prototype.luma = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255; + + r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); + g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); + b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); + + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +}; +Color.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context)); +}; +Color.prototype.toCSS = function (context, doNotCompress) { + var compress = context && context.compress && !doNotCompress, color, alpha; + + // `keyword` is set if this color was originally + // converted from a named color string so we need + // to respect this and try to output named color too. + if (this.keyword) { + return this.keyword; + } + + // 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. + alpha = this.fround(context, this.alpha); + if (alpha < 1) { + return "rgba(" + this.rgb.map(function (c) { + return clamp(Math.round(c), 255); + }).concat(clamp(alpha, 1)) + .join(',' + (compress ? '' : ' ')) + ")"; + } + + color = this.toRGB(); + + if (compress) { + var splitcolor = color.split(''); + + // Convert color to short format + if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { + color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[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. +// +Color.prototype.operate = function (context, op, other) { + var rgb = []; + var alpha = this.alpha * (1 - other.alpha) + other.alpha; + for (var c = 0; c < 3; c++) { + rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]); + } + return new Color(rgb, alpha); +}; +Color.prototype.toRGB = function () { + return toHex(this.rgb); +}; +Color.prototype.toHSL = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2, d = max - min; + + if (max === min) { + h = s = 0; + } else { + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { 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 +Color.prototype.toHSV = function () { + var r = this.rgb[0] / 255, + g = this.rgb[1] / 255, + b = this.rgb[2] / 255, + a = this.alpha; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + + var d = max - min; + if (max === 0) { + s = 0; + } else { + s = d / max; + } + + if (max === min) { + h = 0; + } else { + 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 { h: h * 360, s: s, v: v, a: a }; +}; +Color.prototype.toARGB = function () { + return toHex([this.alpha * 255].concat(this.rgb)); +}; +Color.prototype.compare = function (x) { + return (x.rgb && + x.rgb[0] === this.rgb[0] && + x.rgb[1] === this.rgb[1] && + x.rgb[2] === this.rgb[2] && + x.alpha === this.alpha) ? 0 : undefined; +}; + +Color.fromKeyword = function(keyword) { + var c, key = keyword.toLowerCase(); + if (colors.hasOwnProperty(key)) { + c = new Color(colors[key].slice(1)); + } + else if (key === "transparent") { + c = new Color([0, 0, 0], 0); + } + + if (c) { + c.keyword = keyword; + return c; + } +}; +module.exports = Color; + +},{"../data/colors":9,"./node":64}],45:[function(require,module,exports){ +var Node = require("./node"); + +var Combinator = function (value) { + if (value === ' ') { + this.value = ' '; + this.emptyOrWhitespace = true; + } else { + this.value = value ? value.trim() : ""; + this.emptyOrWhitespace = this.value === ""; + } +}; +Combinator.prototype = new Node(); +Combinator.prototype.type = "Combinator"; +var _noSpaceCombinators = { + '': true, + ' ': true, + '|': true +}; +Combinator.prototype.genCSS = function (context, output) { + var spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' '; + output.add(spaceOrEmpty + this.value + spaceOrEmpty); +}; +module.exports = Combinator; + +},{"./node":64}],46:[function(require,module,exports){ +var Node = require("./node"), + getDebugInfo = require("./debug-info"); + +var Comment = function (value, isLineComment, index, currentFileInfo) { + this.value = value; + this.isLineComment = isLineComment; + this.currentFileInfo = currentFileInfo; +}; +Comment.prototype = new Node(); +Comment.prototype.type = "Comment"; +Comment.prototype.genCSS = function (context, output) { + if (this.debugInfo) { + output.add(getDebugInfo(context, this), this.currentFileInfo, this.index); + } + output.add(this.value); +}; +Comment.prototype.isSilent = function(context) { + var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced), + isCompressed = context.compress && this.value[2] !== "!"; + return this.isLineComment || isReference || isCompressed; +}; +Comment.prototype.markReferenced = function () { + this.isReferenced = true; +}; +Comment.prototype.isRulesetLike = function(root) { + return Boolean(root); +}; +module.exports = Comment; + +},{"./debug-info":48,"./node":64}],47:[function(require,module,exports){ +var Node = require("./node"); + +var Condition = function (op, l, r, i, negate) { + this.op = op.trim(); + this.lvalue = l; + this.rvalue = r; + this.index = i; + this.negate = negate; +}; +Condition.prototype = new Node(); +Condition.prototype.type = "Condition"; +Condition.prototype.accept = function (visitor) { + this.lvalue = visitor.visit(this.lvalue); + this.rvalue = visitor.visit(this.rvalue); +}; +Condition.prototype.eval = function (context) { + var result = (function (op, a, b) { + switch (op) { + case 'and': return a && b; + case 'or': return a || b; + default: + switch (Node.compare(a, b)) { + case -1: return op === '<' || op === '=<' || op === '<='; + case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; + case 1: return op === '>' || op === '>='; + default: return false; + } + } + }) (this.op, this.lvalue.eval(context), this.rvalue.eval(context)); + + return this.negate ? !result : result; +}; +module.exports = Condition; + +},{"./node":64}],48:[function(require,module,exports){ +var debugInfo = function(context, ctx, lineSeperator) { + var result=""; + if (context.dumpLineNumbers && !context.compress) { + switch(context.dumpLineNumbers) { + case 'comments': + result = debugInfo.asComment(ctx); + break; + case 'mediaquery': + result = debugInfo.asMediaQuery(ctx); + break; + case 'all': + result = debugInfo.asComment(ctx) + (lineSeperator || "") + debugInfo.asMediaQuery(ctx); + break; + } + } + return result; +}; + +debugInfo.asComment = function(ctx) { + return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n'; +}; + +debugInfo.asMediaQuery = function(ctx) { + return '@media -sass-debug-info{filename{font-family:' + + ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) { + if (a == '\\') { + a = '\/'; + } + return '\\' + a; + }) + + '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n'; +}; + +module.exports = debugInfo; + +},{}],49:[function(require,module,exports){ +var Node = require("./node"), + contexts = require("../contexts"); + +var DetachedRuleset = function (ruleset, frames) { + this.ruleset = ruleset; + this.frames = frames; +}; +DetachedRuleset.prototype = new Node(); +DetachedRuleset.prototype.type = "DetachedRuleset"; +DetachedRuleset.prototype.evalFirst = true; +DetachedRuleset.prototype.accept = function (visitor) { + this.ruleset = visitor.visit(this.ruleset); +}; +DetachedRuleset.prototype.eval = function (context) { + var frames = this.frames || context.frames.slice(0); + return new DetachedRuleset(this.ruleset, frames); +}; +DetachedRuleset.prototype.callEval = function (context) { + return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context); +}; +module.exports = DetachedRuleset; + +},{"../contexts":8,"./node":64}],50:[function(require,module,exports){ +var Node = require("./node"), + unitConversions = require("../data/unit-conversions"), + Unit = require("./unit"), + Color = require("./color"); + +// +// A number with a unit +// +var Dimension = function (value, unit) { + this.value = parseFloat(value); + this.unit = (unit && unit instanceof Unit) ? unit : + new Unit(unit ? [unit] : undefined); +}; + +Dimension.prototype = new Node(); +Dimension.prototype.type = "Dimension"; +Dimension.prototype.accept = function (visitor) { + this.unit = visitor.visit(this.unit); +}; +Dimension.prototype.eval = function (context) { + return this; +}; +Dimension.prototype.toColor = function () { + return new Color([this.value, this.value, this.value]); +}; +Dimension.prototype.genCSS = function (context, output) { + if ((context && context.strictUnits) && !this.unit.isSingular()) { + throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString()); + } + + var value = this.fround(context, this.value), + strValue = String(value); + + if (value !== 0 && value < 0.000001 && value > -0.000001) { + // would be output 1e-6 etc. + strValue = value.toFixed(20).replace(/0+$/, ""); + } + + if (context && context.compress) { + // Zero values doesn't need a unit + if (value === 0 && this.unit.isLength()) { + output.add(strValue); + return; + } + + // Float values doesn't need a leading zero + if (value > 0 && value < 1) { + strValue = (strValue).substr(1); + } + } + + output.add(strValue); + this.unit.genCSS(context, output); +}; + +// In an operation between two Dimensions, +// we default to the first Dimension's unit, +// so `1px + 2` will yield `3px`. +Dimension.prototype.operate = function (context, op, other) { + /*jshint noempty:false */ + var value = this._operate(context, op, this.value, other.value), + unit = this.unit.clone(); + + if (op === '+' || op === '-') { + if (unit.numerator.length === 0 && unit.denominator.length === 0) { + unit.numerator = other.unit.numerator.slice(0); + unit.denominator = other.unit.denominator.slice(0); + } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) { + // do nothing + } else { + other = other.convertTo(this.unit.usedUnits()); + + if(context.strictUnits && other.unit.toString() !== unit.toString()) { + throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() + + "' and '" + other.unit.toString() + "'."); + } + + value = this._operate(context, op, this.value, other.value); + } + } else if (op === '*') { + unit.numerator = unit.numerator.concat(other.unit.numerator).sort(); + unit.denominator = unit.denominator.concat(other.unit.denominator).sort(); + unit.cancel(); + } else if (op === '/') { + unit.numerator = unit.numerator.concat(other.unit.denominator).sort(); + unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); + unit.cancel(); + } + return new Dimension(value, unit); +}; +Dimension.prototype.compare = function (other) { + var a, b; + + if (!(other instanceof Dimension)) { + return undefined; + } + + 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 undefined; + } + } + + return Node.numericCompare(a.value, b.value); +}; +Dimension.prototype.unify = function () { + return this.convertTo({ length: 'px', duration: 's', angle: 'rad' }); +}; +Dimension.prototype.convertTo = function (conversions) { + var value = this.value, unit = this.unit.clone(), + i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; + + if (typeof conversions === 'string') { + for(i in unitConversions) { + if (unitConversions[i].hasOwnProperty(conversions)) { + derivedConversions = {}; + derivedConversions[i] = conversions; + } + } + conversions = derivedConversions; + } + applyUnit = function (atomicUnit, denominator) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit)) { + if (denominator) { + value = value / (group[atomicUnit] / group[targetUnit]); + } else { + value = value * (group[atomicUnit] / group[targetUnit]); + } + + return targetUnit; + } + + return atomicUnit; + }; + + for (groupName in conversions) { + if (conversions.hasOwnProperty(groupName)) { + targetUnit = conversions[groupName]; + group = unitConversions[groupName]; + + unit.map(applyUnit); + } + } + + unit.cancel(); + + return new Dimension(value, unit); +}; +module.exports = Dimension; + +},{"../data/unit-conversions":11,"./color":44,"./node":64,"./unit":73}],51:[function(require,module,exports){ +var Node = require("./node"), + Ruleset = require("./ruleset"); + +var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { + this.name = name; + this.value = value; + if (rules) { + this.rules = rules; + this.rules.allowImports = true; + } + this.index = index; + this.currentFileInfo = currentFileInfo; + this.debugInfo = debugInfo; +}; + +Directive.prototype = new Node(); +Directive.prototype.type = "Directive"; +Directive.prototype.accept = function (visitor) { + var value = this.value, rules = this.rules; + if (rules) { + this.rules = visitor.visit(rules); + } + if (value) { + this.value = visitor.visit(value); + } +}; +Directive.prototype.isRulesetLike = function() { + return this.rules || !this.isCharset(); +}; +Directive.prototype.isCharset = function() { + return "@charset" === this.name; +}; +Directive.prototype.genCSS = function (context, output) { + var value = this.value, rules = this.rules; + output.add(this.name, this.currentFileInfo, this.index); + if (value) { + output.add(' '); + value.genCSS(context, output); + } + if (rules) { + this.outputRuleset(context, output, [rules]); + } else { + output.add(';'); + } +}; +Directive.prototype.eval = function (context) { + var value = this.value, rules = this.rules; + if (value) { + value = value.eval(context); + } + if (rules) { + rules = rules.eval(context); + rules.root = true; + } + return new Directive(this.name, value, rules, + this.index, this.currentFileInfo, this.debugInfo); +}; +Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); }; +Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); }; +Directive.prototype.rulesets = function () { if (this.rules) return Ruleset.prototype.rulesets.apply(this.rules); }; +Directive.prototype.markReferenced = function () { + var i, rules; + this.isReferenced = true; + if (this.rules) { + rules = this.rules.rules; + for (i = 0; i < rules.length; i++) { + if (rules[i].markReferenced) { + rules[i].markReferenced(); + } + } + } +}; +Directive.prototype.outputRuleset = function (context, output, rules) { + var ruleCnt = rules.length, i; + context.tabLevel = (context.tabLevel | 0) + 1; + + // Compressed + if (context.compress) { + output.add('{'); + for (i = 0; i < ruleCnt; i++) { + rules[i].genCSS(context, output); + } + output.add('}'); + context.tabLevel--; + return; + } + + // Non-compressed + var tabSetStr = '\n' + Array(context.tabLevel).join(" "), tabRuleStr = tabSetStr + " "; + if (!ruleCnt) { + output.add(" {" + tabSetStr + '}'); + } else { + output.add(" {" + tabRuleStr); + rules[0].genCSS(context, output); + for (i = 1; i < ruleCnt; i++) { + output.add(tabRuleStr); + rules[i].genCSS(context, output); + } + output.add(tabSetStr + '}'); + } + + context.tabLevel--; +}; +module.exports = Directive; + +},{"./node":64,"./ruleset":70}],52:[function(require,module,exports){ +var Node = require("./node"), + Paren = require("./paren"), + Combinator = require("./combinator"); + +var Element = function (combinator, value, index, currentFileInfo) { + this.combinator = combinator instanceof Combinator ? + combinator : new Combinator(combinator); + + if (typeof(value) === 'string') { + this.value = value.trim(); + } else if (value) { + this.value = value; + } else { + this.value = ""; + } + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +Element.prototype = new Node(); +Element.prototype.type = "Element"; +Element.prototype.accept = function (visitor) { + var value = this.value; + this.combinator = visitor.visit(this.combinator); + if (typeof value === "object") { + this.value = visitor.visit(value); + } +}; +Element.prototype.eval = function (context) { + return new Element(this.combinator, + this.value.eval ? this.value.eval(context) : this.value, + this.index, + this.currentFileInfo); +}; +Element.prototype.genCSS = function (context, output) { + output.add(this.toCSS(context), this.currentFileInfo, this.index); +}; +Element.prototype.toCSS = function (context) { + context = context || {}; + var value = this.value, firstSelector = context.firstSelector; + if (value instanceof Paren) { + // selector in parens should not be affected by outer selector + // flags (breaks only interpolated selectors - see #1973) + context.firstSelector = true; + } + value = value.toCSS ? value.toCSS(context) : value; + context.firstSelector = firstSelector; + if (value === '' && this.combinator.value.charAt(0) === '&') { + return ''; + } else { + return this.combinator.toCSS(context) + value; + } +}; +module.exports = Element; + +},{"./combinator":45,"./node":64,"./paren":66}],53:[function(require,module,exports){ +var Node = require("./node"), + Paren = require("./paren"), + Comment = require("./comment"); + +var Expression = function (value) { + this.value = value; + if (!value) { + throw new Error("Expression requires a array parameter"); + } +}; +Expression.prototype = new Node(); +Expression.prototype.type = "Expression"; +Expression.prototype.accept = function (visitor) { + this.value = visitor.visitArray(this.value); +}; +Expression.prototype.eval = function (context) { + var returnValue, + inParenthesis = this.parens && !this.parensInOp, + doubleParen = false; + if (inParenthesis) { + context.inParenthesis(); + } + if (this.value.length > 1) { + returnValue = new Expression(this.value.map(function (e) { + return e.eval(context); + })); + } else if (this.value.length === 1) { + if (this.value[0].parens && !this.value[0].parensInOp) { + doubleParen = true; + } + returnValue = this.value[0].eval(context); + } else { + returnValue = this; + } + if (inParenthesis) { + context.outOfParenthesis(); + } + if (this.parens && this.parensInOp && !(context.isMathOn()) && !doubleParen) { + returnValue = new Paren(returnValue); + } + return returnValue; +}; +Expression.prototype.genCSS = function (context, output) { + for(var i = 0; i < this.value.length; i++) { + this.value[i].genCSS(context, output); + if (i + 1 < this.value.length) { + output.add(" "); + } + } +}; +Expression.prototype.throwAwayComments = function () { + this.value = this.value.filter(function(v) { + return !(v instanceof Comment); + }); +}; +module.exports = Expression; + +},{"./comment":46,"./node":64,"./paren":66}],54:[function(require,module,exports){ +var Node = require("./node"); + +var Extend = function Extend(selector, option, index) { + this.selector = selector; + this.option = option; + this.index = index; + this.object_id = Extend.next_id++; + this.parent_ids = [this.object_id]; + + switch(option) { + case "all": + this.allowBefore = true; + this.allowAfter = true; + break; + default: + this.allowBefore = false; + this.allowAfter = false; + break; + } +}; +Extend.next_id = 0; + +Extend.prototype = new Node(); +Extend.prototype.type = "Extend"; +Extend.prototype.accept = function (visitor) { + this.selector = visitor.visit(this.selector); +}; +Extend.prototype.eval = function (context) { + return new Extend(this.selector.eval(context), this.option, this.index); +}; +Extend.prototype.clone = function (context) { + return new Extend(this.selector, this.option, this.index); +}; +Extend.prototype.findSelfSelectors = function (selectors) { + var selfElements = [], + i, + selectorElements; + + for(i = 0; i < selectors.length; 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 > 0 && selectorElements.length && selectorElements[0].combinator.value === "") { + selectorElements[0].combinator.value = ' '; + } + selfElements = selfElements.concat(selectors[i].elements); + } + + this.selfSelectors = [{ elements: selfElements }]; +}; +module.exports = Extend; + +},{"./node":64}],55:[function(require,module,exports){ +var Node = require("./node"), + Media = require("./media"), + URL = require("./url"), + Quoted = require("./quoted"), + Ruleset = require("./ruleset"), + Anonymous = require("./anonymous"); + +// +// 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. +// +var Import = function (path, features, options, index, currentFileInfo) { + this.options = options; + this.index = index; + this.path = path; + this.features = features; + this.currentFileInfo = currentFileInfo; + + if (this.options.less !== undefined || this.options.inline) { + this.css = !this.options.less || this.options.inline; + } else { + var pathValue = this.getPath(); + if (pathValue && /css([\?;].*)?$/.test(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. +// +Import.prototype = new Node(); +Import.prototype.type = "Import"; +Import.prototype.accept = function (visitor) { + if (this.features) { + this.features = visitor.visit(this.features); + } + this.path = visitor.visit(this.path); + if (!this.options.inline && this.root) { + this.root = visitor.visit(this.root); + } +}; +Import.prototype.genCSS = function (context, output) { + if (this.css) { + output.add("@import ", this.currentFileInfo, this.index); + this.path.genCSS(context, output); + if (this.features) { + output.add(" "); + this.features.genCSS(context, output); + } + output.add(';'); + } +}; +Import.prototype.getPath = function () { + if (this.path instanceof Quoted) { + var path = this.path.value; + return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less'; + } else if (this.path instanceof URL) { + return this.path.value.value; + } + return null; +}; +Import.prototype.evalForImport = function (context) { + var path = this.path; + if (path instanceof URL) { + path = path.value; + } + return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo); +}; +Import.prototype.evalPath = function (context) { + var path = this.path.eval(context); + var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; + + if (!(path instanceof URL)) { + if (rootpath) { + var pathValue = path.value; + // Add the base path if the import is relative + if (pathValue && context.isPathRelative(pathValue)) { + path.value = rootpath +pathValue; + } + } + path.value = context.normalizePath(path.value); + } + + return path; +}; +Import.prototype.eval = function (context) { + var ruleset, features = this.features && this.features.eval(context); + + if (this.skip) { + if (typeof this.skip === "function") { + this.skip = this.skip(); + } + if (this.skip) { + return []; + } + } + + if (this.options.inline) { + var contents = new Anonymous(this.root, 0, {filename: this.importedFilename}, true, true); + return this.features ? new Media([contents], this.features.value) : [contents]; + } else if (this.css) { + var newImport = new Import(this.evalPath(context), features, this.options, this.index); + if (!newImport.css && this.error) { + throw this.error; + } + return newImport; + } else { + ruleset = new Ruleset(null, this.root.rules.slice(0)); + + ruleset.evalImports(context); + + return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules; + } +}; +module.exports = Import; + +},{"./anonymous":40,"./media":60,"./node":64,"./quoted":67,"./ruleset":70,"./url":74}],56:[function(require,module,exports){ +var tree = {}; + +tree.Alpha = require('./alpha'); +tree.Color = require('./color'); +tree.Directive = require('./directive'); +tree.DetachedRuleset = require('./detached-ruleset'); +tree.Operation = require('./operation'); +tree.Dimension = require('./dimension'); +tree.Unit = require('./unit'); +tree.Keyword = require('./keyword'); +tree.Variable = require('./variable'); +tree.Ruleset = require('./ruleset'); +tree.Element = require('./element'); +tree.Attribute = require('./attribute'); +tree.Combinator = require('./combinator'); +tree.Selector = require('./selector'); +tree.Quoted = require('./quoted'); +tree.Expression = require('./expression'); +tree.Rule = require('./rule'); +tree.Call = require('./call'); +tree.URL = require('./url'); +tree.Import = require('./import'); +tree.mixin = { + Call: require('./mixin-call'), + Definition: require('./mixin-definition') +}; +tree.Comment = require('./comment'); +tree.Anonymous = require('./anonymous'); +tree.Value = require('./value'); +tree.JavaScript = require('./javascript'); +tree.Assignment = require('./assignment'); +tree.Condition = require('./condition'); +tree.Paren = require('./paren'); +tree.Media = require('./media'); +tree.UnicodeDescriptor = require('./unicode-descriptor'); +tree.Negative = require('./negative'); +tree.Extend = require('./extend'); +tree.RulesetCall = require('./ruleset-call'); + +module.exports = tree; + +},{"./alpha":39,"./anonymous":40,"./assignment":41,"./attribute":42,"./call":43,"./color":44,"./combinator":45,"./comment":46,"./condition":47,"./detached-ruleset":49,"./dimension":50,"./directive":51,"./element":52,"./expression":53,"./extend":54,"./import":55,"./javascript":57,"./keyword":59,"./media":60,"./mixin-call":61,"./mixin-definition":62,"./negative":63,"./operation":65,"./paren":66,"./quoted":67,"./rule":68,"./ruleset":70,"./ruleset-call":69,"./selector":71,"./unicode-descriptor":72,"./unit":73,"./url":74,"./value":75,"./variable":76}],57:[function(require,module,exports){ +var JsEvalNode = require("./js-eval-node"), + Dimension = require("./dimension"), + Quoted = require("./quoted"), + Anonymous = require("./anonymous"); + +var JavaScript = function (string, escaped, index, currentFileInfo) { + this.escaped = escaped; + this.expression = string; + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +JavaScript.prototype = new JsEvalNode(); +JavaScript.prototype.type = "JavaScript"; +JavaScript.prototype.eval = function(context) { + var result = this.evaluateJavaScript(this.expression, context); + + if (typeof(result) === 'number') { + return new Dimension(result); + } else if (typeof(result) === 'string') { + return new Quoted('"' + result + '"', result, this.escaped, this.index); + } else if (Array.isArray(result)) { + return new Anonymous(result.join(', ')); + } else { + return new Anonymous(result); + } +}; + +module.exports = JavaScript; + +},{"./anonymous":40,"./dimension":50,"./js-eval-node":58,"./quoted":67}],58:[function(require,module,exports){ +var Node = require("./node"), + Variable = require("./variable"); + +var JsEvalNode = function() { +}; +JsEvalNode.prototype = new Node(); + +JsEvalNode.prototype.evaluateJavaScript = function (expression, context) { + var result, + that = this, + evalContext = {}; + + if (context.javascriptEnabled !== undefined && !context.javascriptEnabled) { + throw { message: "You are using JavaScript, which has been disabled.", + filename: this.currentFileInfo.filename, + index: this.index }; + } + + expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) { + return that.jsify(new Variable('@' + name, that.index, that.currentFileInfo).eval(context)); + }); + + try { + expression = new Function('return (' + expression + ')'); + } catch (e) { + throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" , + filename: this.currentFileInfo.filename, + index: this.index }; + } + + var variables = context.frames[0].variables(); + for (var k in variables) { + if (variables.hasOwnProperty(k)) { + /*jshint loopfunc:true */ + evalContext[k.slice(1)] = { + value: variables[k].value, + toJS: function () { + return this.value.eval(context).toCSS(); + } + }; + } + } + + try { + result = expression.call(evalContext); + } catch (e) { + throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" , + filename: this.currentFileInfo.filename, + index: this.index }; + } + return result; +}; +JsEvalNode.prototype.jsify = function (obj) { + if (Array.isArray(obj.value) && (obj.value.length > 1)) { + return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']'; + } else { + return obj.toCSS(); + } +}; + +module.exports = JsEvalNode; + +},{"./node":64,"./variable":76}],59:[function(require,module,exports){ +var Node = require("./node"); + +var Keyword = function (value) { this.value = value; }; +Keyword.prototype = new Node(); +Keyword.prototype.type = "Keyword"; +Keyword.prototype.genCSS = function (context, output) { + if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } + output.add(this.value); +}; + +Keyword.True = new Keyword('true'); +Keyword.False = new Keyword('false'); + +module.exports = Keyword; + +},{"./node":64}],60:[function(require,module,exports){ +var Ruleset = require("./ruleset"), + Value = require("./value"), + Element = require("./element"), + Selector = require("./selector"), + Anonymous = require("./anonymous"), + Expression = require("./expression"), + Directive = require("./directive"); + +var Media = function (value, features, index, currentFileInfo) { + this.index = index; + this.currentFileInfo = currentFileInfo; + + var selectors = this.emptySelectors(); + + this.features = new Value(features); + this.rules = [new Ruleset(selectors, value)]; + this.rules[0].allowImports = true; +}; +Media.prototype = new Directive(); +Media.prototype.type = "Media"; +Media.prototype.isRulesetLike = true; +Media.prototype.accept = function (visitor) { + if (this.features) { + this.features = visitor.visit(this.features); + } + if (this.rules) { + this.rules = visitor.visitArray(this.rules); + } +}; +Media.prototype.genCSS = function (context, output) { + output.add('@media ', this.currentFileInfo, this.index); + this.features.genCSS(context, output); + this.outputRuleset(context, output, this.rules); +}; +Media.prototype.eval = function (context) { + if (!context.mediaBlocks) { + context.mediaBlocks = []; + context.mediaPath = []; + } + + var media = new Media(null, [], this.index, this.currentFileInfo); + if(this.debugInfo) { + this.rules[0].debugInfo = this.debugInfo; + media.debugInfo = this.debugInfo; + } + var strictMathBypass = false; + if (!context.strictMath) { + strictMathBypass = true; + context.strictMath = true; + } + try { + media.features = this.features.eval(context); + } + finally { + if (strictMathBypass) { + context.strictMath = false; + } + } + + context.mediaPath.push(media); + context.mediaBlocks.push(media); + + context.frames.unshift(this.rules[0]); + media.rules = [this.rules[0].eval(context)]; + context.frames.shift(); + + context.mediaPath.pop(); + + return context.mediaPath.length === 0 ? media.evalTop(context) : + media.evalNested(context); +}; +//TODO merge with directive +Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); }; +Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); }; +Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); }; +Media.prototype.emptySelectors = function() { + var el = new Element('', '&', this.index, this.currentFileInfo), + sels = [new Selector([el], null, null, this.index, this.currentFileInfo)]; + sels[0].mediaEmpty = true; + return sels; +}; +Media.prototype.markReferenced = function () { + var i, rules = this.rules[0].rules; + this.rules[0].markReferenced(); + this.isReferenced = true; + for (i = 0; i < rules.length; i++) { + if (rules[i].markReferenced) { + rules[i].markReferenced(); + } + } +}; +Media.prototype.evalTop = function (context) { + var result = this; + + // Render all dependent Media blocks. + if (context.mediaBlocks.length > 1) { + var selectors = this.emptySelectors(); + result = new Ruleset(selectors, context.mediaBlocks); + result.multiMedia = true; + } + + delete context.mediaBlocks; + delete context.mediaPath; + + return result; +}; +Media.prototype.evalNested = function (context) { + var i, value, + path = context.mediaPath.concat([this]); + + // Extract the media-query conditions separated with `,` (OR). + for (i = 0; i < path.length; i++) { + value = path[i].features instanceof Value ? + path[i].features.value : path[i].features; + path[i] = Array.isArray(value) ? value : [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 + this.features = new Value(this.permute(path).map(function (path) { + path = path.map(function (fragment) { + return fragment.toCSS ? fragment : new Anonymous(fragment); + }); + + for(i = path.length - 1; i > 0; i--) { + path.splice(i, 0, new Anonymous("and")); + } + + return new Expression(path); + })); + + // Fake a tree-node that doesn't output anything. + return new Ruleset([], []); +}; +Media.prototype.permute = function (arr) { + if (arr.length === 0) { + return []; + } else if (arr.length === 1) { + return arr[0]; + } else { + var result = []; + var rest = this.permute(arr.slice(1)); + for (var i = 0; i < rest.length; i++) { + for (var j = 0; j < arr[0].length; j++) { + result.push([arr[0][j]].concat(rest[i])); + } + } + return result; + } +}; +Media.prototype.bubbleSelectors = function (selectors) { + if (!selectors) + return; + this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])]; +}; +module.exports = Media; + +},{"./anonymous":40,"./directive":51,"./element":52,"./expression":53,"./ruleset":70,"./selector":71,"./value":75}],61:[function(require,module,exports){ +var Node = require("./node"), + Selector = require("./selector"), + MixinDefinition = require("./mixin-definition"), + defaultFunc = require("../functions/default"); + +var MixinCall = function (elements, args, index, currentFileInfo, important) { + this.selector = new Selector(elements); + this.arguments = (args && args.length) ? args : null; + this.index = index; + this.currentFileInfo = currentFileInfo; + this.important = important; +}; +MixinCall.prototype = new Node(); +MixinCall.prototype.type = "MixinCall"; +MixinCall.prototype.accept = function (visitor) { + if (this.selector) { + this.selector = visitor.visit(this.selector); + } + if (this.arguments) { + this.arguments = visitor.visitArray(this.arguments); + } +}; +MixinCall.prototype.eval = function (context) { + var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule, + candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase=-1, + defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter; + + function calcDefGroup(mixin, mixinPath) { + var p, namespace; + + for (f = 0; f < 2; f++) { + conditionResult[f] = true; + defaultFunc.value(f); + for(p = 0; p < mixinPath.length && conditionResult[f]; p++) { + namespace = mixinPath[p]; + if (namespace.matchCondition) { + conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context); + } + } + if (mixin.matchCondition) { + conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context); + } + } + if (conditionResult[0] || conditionResult[1]) { + if (conditionResult[0] != conditionResult[1]) { + return conditionResult[1] ? + defTrue : defFalse; + } + + return defNone; + } + return defFalseEitherCase; + } + + args = this.arguments && this.arguments.map(function (a) { + return { name: a.name, value: a.value.eval(context) }; + }); + + noArgumentsFilter = function(rule) {return rule.matchArgs(null, context);}; + + for (i = 0; i < context.frames.length; i++) { + if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) { + isOneFound = true; + + // 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. + + for (m = 0; m < mixins.length; m++) { + mixin = mixins[m].rule; + mixinPath = mixins[m].path; + isRecursive = false; + for(f = 0; f < context.frames.length; f++) { + if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) { + isRecursive = true; + break; + } + } + if (isRecursive) { + continue; + } + + if (mixin.matchArgs(args, context)) { + candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)}; + + if (candidate.group!==defFalseEitherCase) { + candidates.push(candidate); + } + + match = true; + } + } + + defaultFunc.reset(); + + count = [0, 0, 0]; + for (m = 0; m < candidates.length; m++) { + count[candidates[m].group]++; + } + + if (count[defNone] > 0) { + defaultResult = defFalse; + } else { + defaultResult = defTrue; + if ((count[defTrue] + count[defFalse]) > 1) { + throw { type: 'Runtime', + message: 'Ambiguous use of `default()` found when matching for `' + + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } + } + + for (m = 0; m < candidates.length; m++) { + candidate = candidates[m].group; + if ((candidate === defNone) || (candidate === defaultResult)) { + try { + mixin = candidates[m].mixin; + if (!(mixin instanceof MixinDefinition)) { + originalRuleset = mixin.originalRuleset || mixin; + mixin = new MixinDefinition("", [], mixin.rules, null, false); + mixin.originalRuleset = originalRuleset; + } + Array.prototype.push.apply( + rules, mixin.evalCall(context, args, this.important).rules); + } catch (e) { + throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack }; + } + } + } + + if (match) { + if (!this.currentFileInfo || !this.currentFileInfo.reference) { + for (i = 0; i < rules.length; i++) { + rule = rules[i]; + if (rule.markReferenced) { + rule.markReferenced(); + } + } + } + return rules; + } + } + } + if (isOneFound) { + throw { type: 'Runtime', + message: 'No matching definition was found for `' + this.format(args) + '`', + index: this.index, filename: this.currentFileInfo.filename }; + } else { + throw { type: 'Name', + message: this.selector.toCSS().trim() + " is undefined", + index: this.index, filename: this.currentFileInfo.filename }; + } +}; +MixinCall.prototype.format = function (args) { + return this.selector.toCSS().trim() + '(' + + (args ? args.map(function (a) { + var argValue = ""; + if (a.name) { + argValue += a.name + ":"; + } + if (a.value.toCSS) { + argValue += a.value.toCSS(); + } else { + argValue += "???"; + } + return argValue; + }).join(', ') : "") + ")"; +}; +module.exports = MixinCall; + +},{"../functions/default":17,"./mixin-definition":62,"./node":64,"./selector":71}],62:[function(require,module,exports){ +var Selector = require("./selector"), + Element = require("./element"), + Ruleset = require("./ruleset"), + Rule = require("./rule"), + Expression = require("./expression"), + contexts = require("../contexts"); + +var Definition = function (name, params, rules, condition, variadic, frames) { + this.name = name; + this.selectors = [new Selector([new Element(null, name, this.index, this.currentFileInfo)])]; + this.params = params; + this.condition = condition; + this.variadic = variadic; + this.arity = params.length; + this.rules = rules; + this._lookups = {}; + this.required = params.reduce(function (count, p) { + if (!p.name || (p.name && !p.value)) { return count + 1; } + else { return count; } + }, 0); + this.frames = frames; +}; +Definition.prototype = new Ruleset(); +Definition.prototype.type = "MixinDefinition"; +Definition.prototype.evalFirst = true; +Definition.prototype.accept = function (visitor) { + if (this.params && this.params.length) { + this.params = visitor.visitArray(this.params); + } + this.rules = visitor.visitArray(this.rules); + if (this.condition) { + this.condition = visitor.visit(this.condition); + } +}; +Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) { + /*jshint boss:true */ + var frame = new Ruleset(null, null), + varargs, arg, + params = this.params.slice(0), + i, j, val, name, isNamedFound, argIndex, argsLength = 0; + + mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames)); + + if (args) { + args = args.slice(0); + argsLength = args.length; + + for(i = 0; i < argsLength; i++) { + arg = args[i]; + if (name = (arg && arg.name)) { + isNamedFound = false; + for(j = 0; j < params.length; j++) { + if (!evaldArguments[j] && name === params[j].name) { + evaldArguments[j] = arg.value.eval(context); + frame.prependRule(new Rule(name, arg.value.eval(context))); + isNamedFound = true; + break; + } + } + if (isNamedFound) { + args.splice(i, 1); + i--; + continue; + } else { + throw { type: 'Runtime', message: "Named argument for " + this.name + + ' ' + args[i].name + ' not found' }; + } + } + } + } + argIndex = 0; + for (i = 0; i < params.length; i++) { + if (evaldArguments[i]) { continue; } + + arg = args && args[argIndex]; + + if (name = params[i].name) { + if (params[i].variadic) { + varargs = []; + for (j = argIndex; j < argsLength; j++) { + varargs.push(args[j].value.eval(context)); + } + frame.prependRule(new Rule(name, new Expression(varargs).eval(context))); + } else { + val = arg && arg.value; + if (val) { + val = val.eval(context); + } else if (params[i].value) { + val = params[i].value.eval(mixinEnv); + frame.resetCache(); + } else { + throw { type: 'Runtime', message: "wrong number of arguments for " + this.name + + ' (' + argsLength + ' for ' + this.arity + ')' }; + } + + frame.prependRule(new Rule(name, val)); + evaldArguments[i] = val; + } + } + + if (params[i].variadic && args) { + for (j = argIndex; j < argsLength; j++) { + evaldArguments[j] = args[j].value.eval(context); + } + } + argIndex++; + } + + return frame; +}; +Definition.prototype.eval = function (context) { + return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || context.frames.slice(0)); +}; +Definition.prototype.evalCall = function (context, args, important) { + var _arguments = [], + mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames, + frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments), + rules, ruleset; + + frame.prependRule(new Rule('@arguments', new Expression(_arguments).eval(context))); + + rules = this.rules.slice(0); + + ruleset = new Ruleset(null, rules); + ruleset.originalRuleset = this; + ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames))); + if (important) { + ruleset = this.makeImportant.apply(ruleset); + } + return ruleset; +}; +Definition.prototype.matchCondition = function (args, context) { + if (this.condition && !this.condition.eval( + new contexts.Eval(context, + [this.evalParams(context, new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])] // the parameter variables + .concat(this.frames) // the parent namespace/mixin frames + .concat(context.frames)))) { // the current environment frames + return false; + } + return true; +}; +Definition.prototype.matchArgs = function (args, context) { + var argsLength = (args && args.length) || 0, len; + + if (! this.variadic) { + if (argsLength < this.required) { return false; } + if (argsLength > this.params.length) { return false; } + } else { + if (argsLength < (this.required - 1)) { return false; } + } + + len = Math.min(argsLength, this.arity); + + for (var i = 0; i < len; i++) { + if (!this.params[i].name && !this.params[i].variadic) { + if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) { + return false; + } + } + } + return true; +}; +module.exports = Definition; + +},{"../contexts":8,"./element":52,"./expression":53,"./rule":68,"./ruleset":70,"./selector":71}],63:[function(require,module,exports){ +var Node = require("./node"), + Operation = require("./operation"), + Dimension = require("./dimension"); + +var Negative = function (node) { + this.value = node; +}; +Negative.prototype = new Node(); +Negative.prototype.type = "Negative"; +Negative.prototype.genCSS = function (context, output) { + output.add('-'); + this.value.genCSS(context, output); +}; +Negative.prototype.eval = function (context) { + if (context.isMathOn()) { + return (new Operation('*', [new Dimension(-1), this.value])).eval(context); + } + return new Negative(this.value.eval(context)); +}; +module.exports = Negative; + +},{"./dimension":50,"./node":64,"./operation":65}],64:[function(require,module,exports){ +var Node = function() { +}; +Node.prototype.toCSS = function (context) { + var strs = []; + this.genCSS(context, { + add: function(chunk, fileInfo, index) { + strs.push(chunk); + }, + isEmpty: function () { + return strs.length === 0; + } + }); + return strs.join(''); +}; +Node.prototype.genCSS = function (context, output) { + output.add(this.value); +}; +Node.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +Node.prototype.eval = function () { return this; }; +Node.prototype._operate = function (context, op, a, b) { + switch (op) { + case '+': return a + b; + case '-': return a - b; + case '*': return a * b; + case '/': return a / b; + } +}; +Node.prototype.fround = function(context, value) { + var precision = context && context.numPrecision; + //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded... + return (precision == null) ? value : Number((value + 2e-16).toFixed(precision)); +}; +Node.compare = function (a, b) { + /* returns: + -1: a < b + 0: a = b + 1: a > b + and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */ + + if ((a.compare) && + // for "symmetric results" force toCSS-based comparison + // of Quoted or Anonymous if either value is one of those + !(b.type === "Quoted" || b.type === "Anonymous")) { + return a.compare(b); + } else if (b.compare) { + return -b.compare(a); + } else if (a.type !== b.type) { + return undefined; + } + + a = a.value; + b = b.value; + if (!Array.isArray(a)) { + return a === b ? 0 : undefined; + } + if (a.length !== b.length) { + return undefined; + } + for (var i = 0; i < a.length; i++) { + if (Node.compare(a[i], b[i]) !== 0) { + return undefined; + } + } + return 0; +}; + +Node.numericCompare = function (a, b) { + return a < b ? -1 + : a === b ? 0 + : a > b ? 1 : undefined; +}; +module.exports = Node; + +},{}],65:[function(require,module,exports){ +var Node = require("./node"), + Color = require("./color"), + Dimension = require("./dimension"); + +var Operation = function (op, operands, isSpaced) { + this.op = op.trim(); + this.operands = operands; + this.isSpaced = isSpaced; +}; +Operation.prototype = new Node(); +Operation.prototype.type = "Operation"; +Operation.prototype.accept = function (visitor) { + this.operands = visitor.visit(this.operands); +}; +Operation.prototype.eval = function (context) { + var a = this.operands[0].eval(context), + b = this.operands[1].eval(context); + + if (context.isMathOn()) { + if (a instanceof Dimension && b instanceof Color) { + a = a.toColor(); + } + if (b instanceof Dimension && a instanceof Color) { + b = b.toColor(); + } + if (!a.operate) { + throw { type: "Operation", + message: "Operation on an invalid type" }; + } + + return a.operate(context, this.op, b); + } else { + return new Operation(this.op, [a, b], this.isSpaced); + } +}; +Operation.prototype.genCSS = function (context, output) { + this.operands[0].genCSS(context, output); + if (this.isSpaced) { + output.add(" "); + } + output.add(this.op); + if (this.isSpaced) { + output.add(" "); + } + this.operands[1].genCSS(context, output); +}; + +module.exports = Operation; + +},{"./color":44,"./dimension":50,"./node":64}],66:[function(require,module,exports){ +var Node = require("./node"); + +var Paren = function (node) { + this.value = node; +}; +Paren.prototype = new Node(); +Paren.prototype.type = "Paren"; +Paren.prototype.genCSS = function (context, output) { + output.add('('); + this.value.genCSS(context, output); + output.add(')'); +}; +Paren.prototype.eval = function (context) { + return new Paren(this.value.eval(context)); +}; +module.exports = Paren; + +},{"./node":64}],67:[function(require,module,exports){ +var Node = require("./node"), + JsEvalNode = require("./js-eval-node"), + Variable = require("./variable"); + +var Quoted = function (str, content, escaped, index, currentFileInfo) { + this.escaped = escaped; + this.value = content || ''; + this.quote = str.charAt(0); + this.index = index; + this.currentFileInfo = currentFileInfo; +}; +Quoted.prototype = new JsEvalNode(); +Quoted.prototype.type = "Quoted"; +Quoted.prototype.genCSS = function (context, output) { + if (!this.escaped) { + output.add(this.quote, this.currentFileInfo, this.index); + } + output.add(this.value); + if (!this.escaped) { + output.add(this.quote); + } +}; +Quoted.prototype.eval = function (context) { + var that = this, value = this.value; + var javascriptReplacement = function (_, exp) { + return String(that.evaluateJavaScript(exp, context)); + }; + var interpolationReplacement = function (_, name) { + var v = new Variable('@' + name, that.index, that.currentFileInfo).eval(context, true); + return (v instanceof Quoted) ? v.value : v.toCSS(); + }; + function iterativeReplace(value, regexp, replacementFnc) { + var evaluatedValue = value; + do { + value = evaluatedValue; + evaluatedValue = value.replace(regexp, replacementFnc); + } while (value!==evaluatedValue); + return evaluatedValue; + } + value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement); + value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement); + return new Quoted(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); +}; +Quoted.prototype.compare = function (other) { + // when comparing quoted strings allow the quote to differ + if (other.type === "Quoted" && !this.escaped && !other.escaped) { + return Node.numericCompare(this.value, other.value); + } else { + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; + } +}; +module.exports = Quoted; + +},{"./js-eval-node":58,"./node":64,"./variable":76}],68:[function(require,module,exports){ +var Node = require("./node"), + Value = require("./value"), + Keyword = require("./keyword"); + +var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) { + this.name = name; + this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ?? + this.important = important ? ' ' + important.trim() : ''; + this.merge = merge; + this.index = index; + this.currentFileInfo = currentFileInfo; + this.inline = inline || false; + this.variable = (variable !== undefined) ? variable + : (name.charAt && (name.charAt(0) === '@')); +}; + +function evalName(context, name) { + var value = "", i, n = name.length, + output = {add: function (s) {value += s;}}; + for (i = 0; i < n; i++) { + name[i].eval(context).genCSS(context, output); + } + return value; +} + +Rule.prototype = new Node(); +Rule.prototype.type = "Rule"; +Rule.prototype.genCSS = function (context, output) { + output.add(this.name + (context.compress ? ':' : ': '), this.currentFileInfo, this.index); + try { + this.value.genCSS(context, output); + } + catch(e) { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + throw e; + } + output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this.currentFileInfo, this.index); +}; +Rule.prototype.eval = function (context) { + var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable; + if (typeof name !== "string") { + // expand 'primitive' name directly to get + // things faster (~10% for benchmark.less): + name = (name.length === 1) + && (name[0] instanceof Keyword) + ? name[0].value : evalName(context, name); + variable = false; // never treat expanded interpolation as new variable name + } + if (name === "font" && !context.strictMath) { + strictMathBypass = true; + context.strictMath = true; + } + try { + evaldValue = this.value.eval(context); + + if (!this.variable && evaldValue.type === "DetachedRuleset") { + throw { message: "Rulesets cannot be evaluated on a property.", + index: this.index, filename: this.currentFileInfo.filename }; + } + + return new Rule(name, + evaldValue, + this.important, + this.merge, + this.index, this.currentFileInfo, this.inline, + variable); + } + catch(e) { + if (typeof e.index !== 'number') { + e.index = this.index; + e.filename = this.currentFileInfo.filename; + } + throw e; + } + finally { + if (strictMathBypass) { + context.strictMath = false; + } + } +}; +Rule.prototype.makeImportant = function () { + return new Rule(this.name, + this.value, + "!important", + this.merge, + this.index, this.currentFileInfo, this.inline); +}; + +module.exports = Rule; + +},{"./keyword":59,"./node":64,"./value":75}],69:[function(require,module,exports){ +var Node = require("./node"), + Variable = require("./variable"); + +var RulesetCall = function (variable) { + this.variable = variable; +}; +RulesetCall.prototype = new Node(); +RulesetCall.prototype.type = "RulesetCall"; +RulesetCall.prototype.eval = function (context) { + var detachedRuleset = new Variable(this.variable).eval(context); + return detachedRuleset.callEval(context); +}; +module.exports = RulesetCall; + +},{"./node":64,"./variable":76}],70:[function(require,module,exports){ +var Node = require("./node"), + Rule = require("./rule"), + Selector = require("./selector"), + Element = require("./element"), + contexts = require("../contexts"), + defaultFunc = require("../functions/default"), + getDebugInfo = require("./debug-info"); + +var Ruleset = function (selectors, rules, strictImports) { + this.selectors = selectors; + this.rules = rules; + this._lookups = {}; + this.strictImports = strictImports; +}; +Ruleset.prototype = new Node(); +Ruleset.prototype.type = "Ruleset"; +Ruleset.prototype.isRuleset = true; +Ruleset.prototype.isRulesetLike = true; +Ruleset.prototype.accept = function (visitor) { + if (this.paths) { + visitor.visitArray(this.paths, true); + } else if (this.selectors) { + this.selectors = visitor.visitArray(this.selectors); + } + if (this.rules && this.rules.length) { + this.rules = visitor.visitArray(this.rules); + } +}; +Ruleset.prototype.eval = function (context) { + var thisSelectors = this.selectors, selectors, + selCnt, selector, i, hasOnePassingSelector = false; + + if (thisSelectors && (selCnt = thisSelectors.length)) { + selectors = []; + defaultFunc.error({ + type: "Syntax", + message: "it is currently only allowed in parametric mixin guards," + }); + for (i = 0; i < selCnt; i++) { + selector = thisSelectors[i].eval(context); + selectors.push(selector); + if (selector.evaldCondition) { + hasOnePassingSelector = true; + } + } + defaultFunc.reset(); + } else { + hasOnePassingSelector = true; + } + + var rules = this.rules ? this.rules.slice(0) : null, + ruleset = new Ruleset(selectors, rules, this.strictImports), + rule, subRule; + + ruleset.originalRuleset = this; + ruleset.root = this.root; + ruleset.firstRoot = this.firstRoot; + ruleset.allowImports = this.allowImports; + + if(this.debugInfo) { + ruleset.debugInfo = this.debugInfo; + } + + if (!hasOnePassingSelector) { + rules.length = 0; + } + + // push the current ruleset to the frames stack + var ctxFrames = context.frames; + ctxFrames.unshift(ruleset); + + // currrent selectors + var ctxSelectors = context.selectors; + if (!ctxSelectors) { + context.selectors = ctxSelectors = []; + } + ctxSelectors.unshift(this.selectors); + + // Evaluate imports + if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) { + ruleset.evalImports(context); + } + + // Store the frames around mixin definitions, + // so they can be evaluated like closures when the time comes. + var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0; + for (i = 0; i < rsRuleCnt; i++) { + if (rsRules[i].evalFirst) { + rsRules[i] = rsRules[i].eval(context); + } + } + + var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0; + + // Evaluate mixin calls. + for (i = 0; i < rsRuleCnt; i++) { + if (rsRules[i].type === "MixinCall") { + /*jshint loopfunc:true */ + rules = rsRules[i].eval(context).filter(function(r) { + if ((r instanceof 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 + return !(ruleset.variable(r.name)); + } + return true; + }); + rsRules.splice.apply(rsRules, [i, 1].concat(rules)); + rsRuleCnt += rules.length - 1; + i += rules.length-1; + ruleset.resetCache(); + } else if (rsRules[i].type === "RulesetCall") { + /*jshint loopfunc:true */ + rules = rsRules[i].eval(context).rules.filter(function(r) { + if ((r instanceof Rule) && r.variable) { + // do not pollute the scope at all + return false; + } + return true; + }); + rsRules.splice.apply(rsRules, [i, 1].concat(rules)); + rsRuleCnt += rules.length - 1; + i += rules.length-1; + ruleset.resetCache(); + } + } + + // Evaluate everything else + for (i = 0; i < rsRules.length; i++) { + rule = rsRules[i]; + if (!rule.evalFirst) { + rsRules[i] = rule = rule.eval ? rule.eval(context) : rule; + } + } + + // Evaluate everything else + for (i = 0; i < rsRules.length; i++) { + rule = rsRules[i]; + // for rulesets, check if it is a css guard and can be removed + if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) { + // check if it can be folded in (e.g. & where) + if (rule.selectors[0].isJustParentSelector()) { + rsRules.splice(i--, 1); + + for(var j = 0; j < rule.rules.length; j++) { + subRule = rule.rules[j]; + if (!(subRule instanceof Rule) || !subRule.variable) { + rsRules.splice(++i, 0, subRule); + } + } + } + } + } + + // Pop the stack + ctxFrames.shift(); + ctxSelectors.shift(); + + if (context.mediaBlocks) { + for (i = mediaBlockCount; i < context.mediaBlocks.length; i++) { + context.mediaBlocks[i].bubbleSelectors(selectors); + } + } + + return ruleset; +}; +Ruleset.prototype.evalImports = function(context) { + var rules = this.rules, i, importRules; + if (!rules) { return; } + + for (i = 0; i < rules.length; i++) { + if (rules[i].type === "Import") { + importRules = rules[i].eval(context); + if (importRules && importRules.length) { + rules.splice.apply(rules, [i, 1].concat(importRules)); + i+= importRules.length-1; + } else { + rules.splice(i, 1, importRules); + } + this.resetCache(); + } + } +}; +Ruleset.prototype.makeImportant = function() { + return new Ruleset(this.selectors, this.rules.map(function (r) { + if (r.makeImportant) { + return r.makeImportant(); + } else { + return r; + } + }), this.strictImports); +}; +Ruleset.prototype.matchArgs = function (args) { + return !args || args.length === 0; +}; +// lets you call a css selector with a guard +Ruleset.prototype.matchCondition = function (args, context) { + var lastSelector = this.selectors[this.selectors.length-1]; + if (!lastSelector.evaldCondition) { + return false; + } + if (lastSelector.condition && + !lastSelector.condition.eval( + new contexts.Eval(context, + context.frames))) { + return false; + } + return true; +}; +Ruleset.prototype.resetCache = function () { + this._rulesets = null; + this._variables = null; + this._lookups = {}; +}; +Ruleset.prototype.variables = function () { + if (!this._variables) { + this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) { + if (r instanceof Rule && r.variable === true) { + hash[r.name] = r; + } + return hash; + }, {}); + } + return this._variables; +}; +Ruleset.prototype.variable = function (name) { + return this.variables()[name]; +}; +Ruleset.prototype.rulesets = function () { + if (!this.rules) { return null; } + + var filtRules = [], rules = this.rules, cnt = rules.length, + i, rule; + + for (i = 0; i < cnt; i++) { + rule = rules[i]; + if (rule.isRuleset) { + filtRules.push(rule); + } + } + + return filtRules; +}; +Ruleset.prototype.prependRule = function (rule) { + var rules = this.rules; + if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; } +}; +Ruleset.prototype.find = function (selector, self, filter) { + self = self || this; + var rules = [], match, foundMixins, + key = selector.toCSS(); + + if (key in this._lookups) { return this._lookups[key]; } + + this.rulesets().forEach(function (rule) { + if (rule !== self) { + for (var j = 0; j < rule.selectors.length; j++) { + match = selector.match(rule.selectors[j]); + if (match) { + if (selector.elements.length > match) { + if (!filter || filter(rule)) { + foundMixins = rule.find(new Selector(selector.elements.slice(match)), self, filter); + for (var i = 0; i < foundMixins.length; ++i) { + foundMixins[i].path.push(rule); + } + Array.prototype.push.apply(rules, foundMixins); + } + } else { + rules.push({ rule: rule, path: []}); + } + break; + } + } + } + }); + this._lookups[key] = rules; + return rules; +}; +Ruleset.prototype.genCSS = function (context, output) { + var i, j, + charsetRuleNodes = [], + ruleNodes = [], + rulesetNodes = [], + rulesetNodeCnt, + debugInfo, // Line number debugging + rule, + path; + + context.tabLevel = (context.tabLevel || 0); + + if (!this.root) { + context.tabLevel++; + } + + var tabRuleStr = context.compress ? '' : Array(context.tabLevel + 1).join(" "), + tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "), + sep; + + function isRulesetLikeNode(rule, root) { + // if it has nested rules, then it should be treated like a ruleset + // medias and comments do not have nested rules, but should be treated like rulesets anyway + // some directives and anonymous nodes are ruleset like, others are not + if (typeof rule.isRulesetLike === "boolean") + { + return rule.isRulesetLike; + } else if (typeof rule.isRulesetLike === "function") + { + return rule.isRulesetLike(root); + } + + //anything else is assumed to be a rule + return false; + } + + for (i = 0; i < this.rules.length; i++) { + rule = this.rules[i]; + if (isRulesetLikeNode(rule, this.root)) { + rulesetNodes.push(rule); + } else { + //charsets should float on top of everything + if (rule.isCharset && rule.isCharset()) { + charsetRuleNodes.push(rule); + } else { + ruleNodes.push(rule); + } + } + } + ruleNodes = charsetRuleNodes.concat(ruleNodes); + + // If this is the root node, we don't render + // a selector, or {}. + if (!this.root) { + debugInfo = getDebugInfo(context, this, tabSetStr); + + if (debugInfo) { + output.add(debugInfo); + output.add(tabSetStr); + } + + var paths = this.paths, pathCnt = paths.length, + pathSubCnt; + + sep = context.compress ? ',' : (',\n' + tabSetStr); + + for (i = 0; i < pathCnt; i++) { + path = paths[i]; + if (!(pathSubCnt = path.length)) { continue; } + if (i > 0) { output.add(sep); } + + context.firstSelector = true; + path[0].genCSS(context, output); + + context.firstSelector = false; + for (j = 1; j < pathSubCnt; j++) { + path[j].genCSS(context, output); + } + } + + output.add((context.compress ? '{' : ' {\n') + tabRuleStr); + } + + // Compile rules and rulesets + for (i = 0; i < ruleNodes.length; 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.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) { + context.lastRule = true; + } + + if (rule.genCSS) { + rule.genCSS(context, output); + } else if (rule.value) { + output.add(rule.value.toString()); + } + + if (!context.lastRule) { + output.add(context.compress ? '' : ('\n' + tabRuleStr)); + } else { + context.lastRule = false; + } + } + + if (!this.root) { + output.add((context.compress ? '}' : '\n' + tabSetStr + '}')); + context.tabLevel--; + } + + sep = (context.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr); + rulesetNodeCnt = rulesetNodes.length; + if (rulesetNodeCnt) { + if (ruleNodes.length && sep) { output.add(sep); } + rulesetNodes[0].genCSS(context, output); + for (i = 1; i < rulesetNodeCnt; i++) { + if (sep) { output.add(sep); } + rulesetNodes[i].genCSS(context, output); + } + } + + if (!output.isEmpty() && !context.compress && this.firstRoot) { + output.add('\n'); + } +}; +Ruleset.prototype.markReferenced = function () { + if (!this.selectors) { + return; + } + for (var s = 0; s < this.selectors.length; s++) { + this.selectors[s].markReferenced(); + } +}; +Ruleset.prototype.joinSelectors = function (paths, context, selectors) { + for (var s = 0; s < selectors.length; s++) { + this.joinSelector(paths, context, selectors[s]); + } +}; +Ruleset.prototype.joinSelector = function (paths, context, selector) { + + var i, j, k, + hasParentSelector, newSelectors, el, sel, parentSel, + newSelectorPath, afterParentJoin, newJoinedSelector, + newJoinedSelectorEmpty, lastSelector, currentElements, + selectorsMultiplied; + + for (i = 0; i < selector.elements.length; i++) { + el = selector.elements[i]; + if (el.value === '&') { + hasParentSelector = true; + } + } + + if (!hasParentSelector) { + if (context.length > 0) { + for (i = 0; i < context.length; i++) { + paths.push(context[i].concat(selector)); + } + } + else { + paths.push([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 = []; + // 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 = [[]]; + + for (i = 0; i < selector.elements.length; i++) { + el = selector.elements[i]; + // non parent reference elements just get added + if (el.value !== "&") { + currentElements.push(el); + } else { + // the new list of selectors to add + selectorsMultiplied = []; + + // merge the current list of non parent selector elements + // on to the current list of selectors to add + if (currentElements.length > 0) { + this.mergeElementsOnToSelectors(currentElements, newSelectors); + } + + // loop through our current selectors + for (j = 0; j < newSelectors.length; j++) { + sel = newSelectors[j]; + // 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.length === 0) { + // the combinator used on el should now be applied to the next element instead so that + // it is not lost + if (sel.length > 0) { + sel[0].elements = sel[0].elements.slice(0); + sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo)); + } + selectorsMultiplied.push(sel); + } + else { + // and the parent selectors + for (k = 0; k < context.length; k++) { + parentSel = context[k]; + // We need to put the current selectors + // then join the last selector's elements on to the parents selectors + + // our new selector path + newSelectorPath = []; + // selectors from the parent after the join + afterParentJoin = []; + 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.length > 0) { + newSelectorPath = sel.slice(0); + lastSelector = newSelectorPath.pop(); + newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0)); + newJoinedSelectorEmpty = false; + } + else { + newJoinedSelector = selector.createDerived([]); + } + + //put together the parent selectors after the join + if (parentSel.length > 1) { + afterParentJoin = afterParentJoin.concat(parentSel.slice(1)); + } + + if (parentSel.length > 0) { + newJoinedSelectorEmpty = false; + + // /deep/ is a combinator that is valid without anything in front of it + // so if the & does not have a combinator that is "" or " " then + // and there is a combinator on the parent, then grab that. + // this also allows + a { & .b { .a & { ... though not sure why you would want to do that + var combinator = el.combinator, + parentEl = parentSel[0].elements[0]; + if (combinator.emptyOrWhitespace && !parentEl.combinator.emptyOrWhitespace) { + combinator = parentEl.combinator; + } + // join the elements so far with the first part of the parent + newJoinedSelector.elements.push(new Element(combinator, parentEl.value, el.index, el.currentFileInfo)); + newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1)); + } + + if (!newJoinedSelectorEmpty) { + // now add the joined selector + newSelectorPath.push(newJoinedSelector); + } + + // and the rest of the parent + newSelectorPath = newSelectorPath.concat(afterParentJoin); + + // add that to our new set of selectors + selectorsMultiplied.push(newSelectorPath); + } + } + } + + // our new selectors has been multiplied, so reset the state + newSelectors = selectorsMultiplied; + currentElements = []; + } + } + + // if we have any elements left over (e.g. .a& .b == .b) + // add them on to all the current selectors + if (currentElements.length > 0) { + this.mergeElementsOnToSelectors(currentElements, newSelectors); + } + + for (i = 0; i < newSelectors.length; i++) { + if (newSelectors[i].length > 0) { + paths.push(newSelectors[i]); + } + } +}; +Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) { + var i, sel; + + if (selectors.length === 0) { + selectors.push([ new Selector(elements) ]); + return; + } + + for (i = 0; i < selectors.length; i++) { + sel = selectors[i]; + + // if the previous thing in sel is a parent this needs to join on to it + if (sel.length > 0) { + sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements)); + } + else { + sel.push(new Selector(elements)); + } + } +}; +module.exports = Ruleset; + +},{"../contexts":8,"../functions/default":17,"./debug-info":48,"./element":52,"./node":64,"./rule":68,"./selector":71}],71:[function(require,module,exports){ +var Node = require("./node"); + +var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { + this.elements = elements; + this.extendList = extendList; + this.condition = condition; + this.currentFileInfo = currentFileInfo || {}; + this.isReferenced = isReferenced; + if (!condition) { + this.evaldCondition = true; + } +}; +Selector.prototype = new Node(); +Selector.prototype.type = "Selector"; +Selector.prototype.accept = function (visitor) { + if (this.elements) { + this.elements = visitor.visitArray(this.elements); + } + if (this.extendList) { + this.extendList = visitor.visitArray(this.extendList); + } + if (this.condition) { + this.condition = visitor.visit(this.condition); + } +}; +Selector.prototype.createDerived = function(elements, extendList, evaldCondition) { + evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; + var newSelector = new Selector(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); + newSelector.evaldCondition = evaldCondition; + newSelector.mediaEmpty = this.mediaEmpty; + return newSelector; +}; +Selector.prototype.match = function (other) { + var elements = this.elements, + len = elements.length, + olen, i; + + other.CacheElements(); + + olen = other._elements.length; + if (olen === 0 || len < olen) { + return 0; + } else { + for (i = 0; i < olen; i++) { + if (elements[i].value !== other._elements[i]) { + return 0; + } + } + } + + return olen; // return number of matched elements +}; +Selector.prototype.CacheElements = function() { + if (this._elements) + return; + + var elements = this.elements.map( function(v) { + return v.combinator.value + (v.value.value || v.value); + }).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g); + + if (elements) { + if (elements[0] === "&") { + elements.shift(); + } + } else { + elements = []; + } + + this._elements = elements; +}; +Selector.prototype.isJustParentSelector = function() { + return !this.mediaEmpty && + this.elements.length === 1 && + this.elements[0].value === '&' && + (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === ''); +}; +Selector.prototype.eval = function (context) { + var evaldCondition = this.condition && this.condition.eval(context), + elements = this.elements, extendList = this.extendList; + + elements = elements && elements.map(function (e) { return e.eval(context); }); + extendList = extendList && extendList.map(function(extend) { return extend.eval(context); }); + + return this.createDerived(elements, extendList, evaldCondition); +}; +Selector.prototype.genCSS = function (context, output) { + var i, element; + if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") { + output.add(' ', this.currentFileInfo, this.index); + } + if (!this._css) { + //TODO caching? speed comparison? + for(i = 0; i < this.elements.length; i++) { + element = this.elements[i]; + element.genCSS(context, output); + } + } +}; +Selector.prototype.markReferenced = function () { + this.isReferenced = true; +}; +Selector.prototype.getIsReferenced = function() { + return !this.currentFileInfo.reference || this.isReferenced; +}; +Selector.prototype.getIsOutput = function() { + return this.evaldCondition; +}; +module.exports = Selector; + +},{"./node":64}],72:[function(require,module,exports){ +var Node = require("./node"); + +var UnicodeDescriptor = function (value) { + this.value = value; +}; +UnicodeDescriptor.prototype = new Node(); +UnicodeDescriptor.prototype.type = "UnicodeDescriptor"; + +module.exports = UnicodeDescriptor; + +},{"./node":64}],73:[function(require,module,exports){ +var Node = require("./node"), + unitConversions = require("../data/unit-conversions"); + +var Unit = function (numerator, denominator, backupUnit) { + this.numerator = numerator ? numerator.slice(0).sort() : []; + this.denominator = denominator ? denominator.slice(0).sort() : []; + this.backupUnit = backupUnit; +}; + +Unit.prototype = new Node(); +Unit.prototype.type = "Unit"; +Unit.prototype.clone = function () { + return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit); +}; +Unit.prototype.genCSS = function (context, output) { + if (this.numerator.length >= 1) { + output.add(this.numerator[0]); + } else + if (this.denominator.length >= 1) { + output.add(this.denominator[0]); + } else + if ((!context || !context.strictUnits) && this.backupUnit) { + output.add(this.backupUnit); + } +}; +Unit.prototype.toString = function () { + var i, returnStr = this.numerator.join("*"); + for (i = 0; i < this.denominator.length; i++) { + returnStr += "/" + this.denominator[i]; + } + return returnStr; +}; +Unit.prototype.compare = function (other) { + return this.is(other.toString()) ? 0 : undefined; +}; +Unit.prototype.is = function (unitString) { + return this.toString().toUpperCase() === unitString.toUpperCase(); +}; +Unit.prototype.isLength = function () { + return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/)); +}; +Unit.prototype.isEmpty = function () { + return this.numerator.length === 0 && this.denominator.length === 0; +}; +Unit.prototype.isSingular = function() { + return this.numerator.length <= 1 && this.denominator.length === 0; +}; +Unit.prototype.map = function(callback) { + var i; + + for (i = 0; i < this.numerator.length; i++) { + this.numerator[i] = callback(this.numerator[i], false); + } + + for (i = 0; i < this.denominator.length; i++) { + this.denominator[i] = callback(this.denominator[i], true); + } +}; +Unit.prototype.usedUnits = function() { + var group, result = {}, mapUnit; + + mapUnit = function (atomicUnit) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { + result[groupName] = atomicUnit; + } + + return atomicUnit; + }; + + for (var groupName in unitConversions) { + if (unitConversions.hasOwnProperty(groupName)) { + group = unitConversions[groupName]; + + this.map(mapUnit); + } + } + + return result; +}; +Unit.prototype.cancel = function () { + var counter = {}, atomicUnit, i, backup; + + for (i = 0; i < this.numerator.length; i++) { + atomicUnit = this.numerator[i]; + if (!backup) { + backup = atomicUnit; + } + counter[atomicUnit] = (counter[atomicUnit] || 0) + 1; + } + + for (i = 0; i < this.denominator.length; i++) { + atomicUnit = this.denominator[i]; + if (!backup) { + backup = atomicUnit; + } + counter[atomicUnit] = (counter[atomicUnit] || 0) - 1; + } + + this.numerator = []; + this.denominator = []; + + for (atomicUnit in counter) { + if (counter.hasOwnProperty(atomicUnit)) { + var count = counter[atomicUnit]; + + if (count > 0) { + for (i = 0; i < count; i++) { + this.numerator.push(atomicUnit); + } + } else if (count < 0) { + for (i = 0; i < -count; i++) { + this.denominator.push(atomicUnit); + } + } + } + } + + if (this.numerator.length === 0 && this.denominator.length === 0 && backup) { + this.backupUnit = backup; + } + + this.numerator.sort(); + this.denominator.sort(); +}; +module.exports = Unit; + +},{"../data/unit-conversions":11,"./node":64}],74:[function(require,module,exports){ +var Node = require("./node"); + +var URL = function (val, index, currentFileInfo, isEvald) { + this.value = val; + this.currentFileInfo = currentFileInfo; + this.index = index; + this.isEvald = isEvald; +}; +URL.prototype = new Node(); +URL.prototype.type = "Url"; +URL.prototype.accept = function (visitor) { + this.value = visitor.visit(this.value); +}; +URL.prototype.genCSS = function (context, output) { + output.add("url("); + this.value.genCSS(context, output); + output.add(")"); +}; +URL.prototype.eval = function (context) { + var val = this.value.eval(context), + rootpath; + + if (!this.isEvald) { + // Add the base path if the URL is relative + rootpath = this.currentFileInfo && this.currentFileInfo.rootpath; + if (rootpath && typeof val.value === "string" && context.isPathRelative(val.value)) { + if (!val.quote) { + rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; }); + } + val.value = rootpath + val.value; + } + + val.value = context.normalizePath(val.value); + + // Add url args if enabled + if (context.urlArgs) { + if (!val.value.match(/^\s*data:/)) { + var delimiter = val.value.indexOf('?') === -1 ? '?' : '&'; + var urlArgs = delimiter + context.urlArgs; + if (val.value.indexOf('#') !== -1) { + val.value = val.value.replace('#', urlArgs + '#'); + } else { + val.value += urlArgs; + } + } + } + } + + return new URL(val, this.index, this.currentFileInfo, true); +}; +module.exports = URL; + +},{"./node":64}],75:[function(require,module,exports){ +var Node = require("./node"); + +var Value = function (value) { + this.value = value; + if (!value) { + throw new Error("Value requires an array argument"); + } +}; +Value.prototype = new Node(); +Value.prototype.type = "Value"; +Value.prototype.accept = function (visitor) { + if (this.value) { + this.value = visitor.visitArray(this.value); + } +}; +Value.prototype.eval = function (context) { + if (this.value.length === 1) { + return this.value[0].eval(context); + } else { + return new Value(this.value.map(function (v) { + return v.eval(context); + })); + } +}; +Value.prototype.genCSS = function (context, output) { + var i; + for(i = 0; i < this.value.length; i++) { + this.value[i].genCSS(context, output); + if (i+1 < this.value.length) { + output.add((context && context.compress) ? ',' : ', '); + } + } +}; +module.exports = Value; + +},{"./node":64}],76:[function(require,module,exports){ +var Node = require("./node"); + +var Variable = function (name, index, currentFileInfo) { + this.name = name; + this.index = index; + this.currentFileInfo = currentFileInfo || {}; +}; +Variable.prototype = new Node(); +Variable.prototype.type = "Variable"; +Variable.prototype.eval = function (context) { + var variable, name = this.name; + + if (name.indexOf('@@') === 0) { + name = '@' + new Variable(name.slice(1), this.index, this.currentFileInfo).eval(context).value; + } + + if (this.evaluating) { + throw { type: 'Name', + message: "Recursive variable definition for " + name, + filename: this.currentFileInfo.filename, + index: this.index }; + } + + this.evaluating = true; + + variable = this.find(context.frames, function (frame) { + var v = frame.variable(name); + if (v) { + return v.value.eval(context); + } + }); + if (variable) { + this.evaluating = false; + return variable; + } else { + throw { type: 'Name', + message: "variable " + name + " is undefined", + filename: this.currentFileInfo.filename, + index: this.index }; + } +}; +Variable.prototype.find = function (obj, fun) { + for (var i = 0, r; i < obj.length; i++) { + r = fun.call(obj, obj[i]); + if (r) { return r; } + } + return null; +}; +module.exports = Variable; + +},{"./node":64}],77:[function(require,module,exports){ +module.exports = { + getLocation: function(index, inputStream) { + var n = index + 1, + line = null, + column = -1; + + while (--n >= 0 && inputStream.charAt(n) !== '\n') { + column++; + } + + if (typeof index === 'number') { + line = (inputStream.slice(0, index).match(/\n/g) || "").length; + } + + return { + line: line, + column: column + }; + } +}; + +},{}],78:[function(require,module,exports){ +var tree = require("../tree"), + Visitor = require("./visitor"); + +/*jshint loopfunc:true */ + +var ExtendFinderVisitor = function() { + this._visitor = new Visitor(this); + this.contexts = []; + this.allExtendsStack = [[]]; +}; + +ExtendFinderVisitor.prototype = { + run: function (root) { + root = this._visitor.visit(root); + root.allExtends = this.allExtendsStack[0]; + return root; + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitRuleset: function (rulesetNode, visitArgs) { + if (rulesetNode.root) { + return; + } + + var i, j, extend, allSelectorsExtendList = [], extendList; + + // get &:extend(.a); rules which apply to all selectors in this ruleset + var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0; + for(i = 0; i < ruleCnt; i++) { + if (rulesetNode.rules[i] instanceof tree.Extend) { + allSelectorsExtendList.push(rules[i]); + 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 + var paths = rulesetNode.paths; + for(i = 0; i < paths.length; i++) { + var selectorPath = paths[i], + selector = selectorPath[selectorPath.length - 1], + selExtendList = selector.extendList; + + extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList) + : allSelectorsExtendList; + + if (extendList) { + extendList = extendList.map(function(allSelectorsExtend) { + return allSelectorsExtend.clone(); + }); + } + + for(j = 0; j < extendList.length; j++) { + this.foundExtends = true; + extend = extendList[j]; + extend.findSelfSelectors(selectorPath); + extend.ruleset = rulesetNode; + if (j === 0) { extend.firstExtendOnThisSelectorPath = true; } + this.allExtendsStack[this.allExtendsStack.length-1].push(extend); + } + } + + this.contexts.push(rulesetNode.selectors); + }, + visitRulesetOut: function (rulesetNode) { + if (!rulesetNode.root) { + this.contexts.length = this.contexts.length - 1; + } + }, + visitMedia: function (mediaNode, visitArgs) { + mediaNode.allExtends = []; + this.allExtendsStack.push(mediaNode.allExtends); + }, + visitMediaOut: function (mediaNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + }, + visitDirective: function (directiveNode, visitArgs) { + directiveNode.allExtends = []; + this.allExtendsStack.push(directiveNode.allExtends); + }, + visitDirectiveOut: function (directiveNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + } +}; + +var ProcessExtendsVisitor = function() { + this._visitor = new Visitor(this); +}; + +ProcessExtendsVisitor.prototype = { + run: function(root) { + var extendFinder = new ExtendFinderVisitor(); + extendFinder.run(root); + if (!extendFinder.foundExtends) { return root; } + root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends)); + this.allExtendsStack = [root.allExtends]; + return this._visitor.visit(root); + }, + doExtendChaining: function (extendsList, extendsListTarget, iterationCount) { + // + // 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 + + var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend; + + iterationCount = iterationCount || 0; + + //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; extendIndex < extendsList.length; extendIndex++){ + for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){ + + extend = extendsList[extendIndex]; + targetExtend = extendsListTarget[targetExtendIndex]; + + // look for circular references + if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; } + + // find a match in the target extends self selector (the bit before :extend) + selectorPath = [targetExtend.selfSelectors[0]]; + matches = extendVisitor.findMatch(extend, selectorPath); + + if (matches.length) { + + // we found a match, so for each self selector.. + extend.selfSelectors.forEach(function(selfSelector) { + + // process the extend as usual + newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector); + + // but now we create a new extend from it + newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0); + newExtend.selfSelectors = newSelector; + + // add the extend onto the list of extends for that selector + newSelector[newSelector.length-1].extendList = [newExtend]; + + // record that we need to add it. + extendsToAdd.push(newExtend); + newExtend.ruleset = targetExtend.ruleset; + + //remember its parents for circular references + newExtend.parent_ids = newExtend.parent_ids.concat(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.push(newSelector); + } + }); + } + } + } + + if (extendsToAdd.length) { + // try to detect circular references to stop a stack overflow. + // may no longer be needed. + this.extendChainCount++; + if (iterationCount > 100) { + var selectorOne = "{unable to calculate}"; + var selectorTwo = "{unable to calculate}"; + try + { + selectorOne = extendsToAdd[0].selfSelectors[0].toCSS(); + selectorTwo = extendsToAdd[0].selector.toCSS(); + } + catch(e) {} + throw {message: "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... + return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1)); + } else { + return extendsToAdd; + } + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitSelector: function (selectorNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitRuleset: function (rulesetNode, visitArgs) { + if (rulesetNode.root) { + return; + } + var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath; + + // look at each selector path in the ruleset, find any extend matches and then copy, find and replace + + for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) { + for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) { + selectorPath = rulesetNode.paths[pathIndex]; + + // extending extends happens initially, before the main pass + if (rulesetNode.extendOnEveryPath) { continue; } + var extendList = selectorPath[selectorPath.length-1].extendList; + if (extendList && extendList.length) { continue; } + + matches = this.findMatch(allExtends[extendIndex], selectorPath); + + if (matches.length) { + + allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) { + selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector)); + }); + } + } + } + rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd); + }, + findMatch: function (extend, haystackSelectorPath) { + // + // 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 + // + var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement, + targetCombinator, i, + extendVisitor = this, + needleElements = extend.selector.elements, + potentialMatches = [], potentialMatch, matches = []; + + // loop through the haystack elements + for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) { + hackstackSelector = haystackSelectorPath[haystackSelectorIndex]; + + for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; 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.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator}); + } + + for(i = 0; i < potentialMatches.length; i++) { + potentialMatch = potentialMatches[i]; + + // 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.value; + if (targetCombinator === '' && hackstackElementIndex === 0) { + targetCombinator = ' '; + } + + // if we don't match, null our match to indicate failure + if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) || + (potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) { + potentialMatch = null; + } else { + potentialMatch.matched++; + } + + // if we are still valid and have finished, test whether we have elements after and whether these are allowed + if (potentialMatch) { + potentialMatch.finished = potentialMatch.matched === needleElements.length; + if (potentialMatch.finished && + (!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) { + 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 = needleElements.length; + potentialMatch.endPathIndex = haystackSelectorIndex; + potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match + potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again + matches.push(potentialMatch); + } + } else { + potentialMatches.splice(i, 1); + i--; + } + } + } + } + return matches; + }, + isElementValuesEqual: function(elementValue1, elementValue2) { + if (typeof elementValue1 === "string" || typeof elementValue2 === "string") { + return elementValue1 === elementValue2; + } + if (elementValue1 instanceof tree.Attribute) { + 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; + elementValue2 = elementValue2.value.value || elementValue2.value; + return elementValue1 === elementValue2; + } + elementValue1 = elementValue1.value; + elementValue2 = elementValue2.value; + if (elementValue1 instanceof tree.Selector) { + if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) { + return false; + } + for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) { + path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathElementIndex = 0; + currentSelectorPathIndex++; + } + + newElements = selector.elements + .slice(currentSelectorPathElementIndex, match.index) + .concat([firstElement]) + .concat(replacementSelector.elements.slice(1)); + + if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) { + path[path.length - 1].elements = + path[path.length - 1].elements.concat(newElements); + } else { + path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex)); + + path.push(new tree.Selector( + newElements + )); + } + currentSelectorPathIndex = match.endPathIndex; + currentSelectorPathElementIndex = match.endPathElementIndex; + if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) { + currentSelectorPathElementIndex = 0; + currentSelectorPathIndex++; + } + } + + if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) { + path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex)); + currentSelectorPathIndex++; + } + + path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length)); + + return path; + }, + visitRulesetOut: function (rulesetNode) { + }, + visitMedia: function (mediaNode, visitArgs) { + var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); + newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends)); + this.allExtendsStack.push(newAllExtends); + }, + visitMediaOut: function (mediaNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + }, + visitDirective: function (directiveNode, visitArgs) { + var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]); + newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends)); + this.allExtendsStack.push(newAllExtends); + }, + visitDirectiveOut: function (directiveNode) { + this.allExtendsStack.length = this.allExtendsStack.length - 1; + } +}; + +module.exports = ProcessExtendsVisitor; + +},{"../tree":56,"./visitor":83}],79:[function(require,module,exports){ +var contexts = require("../contexts"), + Visitor = require("./visitor"); + +var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) { + this._visitor = new Visitor(this); + this._importer = importer; + this._finish = finish; + this.context = evalEnv || new contexts.Eval(); + this.importCount = 0; + this.onceFileDetectionMap = onceFileDetectionMap || {}; + this.recursionDetector = {}; + if (recursionDetector) { + for(var fullFilename in recursionDetector) { + if (recursionDetector.hasOwnProperty(fullFilename)) { + this.recursionDetector[fullFilename] = true; + } + } + } +}; + +ImportVisitor.prototype = { + isReplacing: true, + run: function (root) { + var error; + try { + // process the contents + this._visitor.visit(root); + } + catch(e) { + error = e; + } + + this.isFinished = true; + + if (this.importCount === 0) { + this._finish(error); + } + }, + visitImport: function (importNode, visitArgs) { + var importVisitor = this, + evaldImportNode, + inlineCSS = importNode.options.inline; + + if (!importNode.css || inlineCSS) { + + try { + evaldImportNode = importNode.evalForImport(this.context); + } catch(e){ + if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } + // attempt to eval properly and treat as css + importNode.css = true; + // if that fails, this error will be thrown + importNode.error = e; + } + + if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { + importNode = evaldImportNode; + this.importCount++; + var context = new contexts.Eval(this.context, this.context.frames.slice(0)); + + if (importNode.options.multiple) { + context.importMultiple = true; + } + + this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) { + if (e && !e.filename) { + e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; + } + + var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; + if (!context.importMultiple) { + if (duplicateImport) { + importNode.skip = true; + } else { + importNode.skip = function() { + if (fullPath in importVisitor.onceFileDetectionMap) { + return true; + } + importVisitor.onceFileDetectionMap[fullPath] = true; + return false; + }; + } + } + + var subFinish = function(e) { + importVisitor.importCount--; + + if (importVisitor.importCount === 0 && importVisitor.isFinished) { + importVisitor._finish(e); + } + }; + + if (root) { + importNode.root = root; + importNode.importedFilename = fullPath; + + if (!inlineCSS && (context.importMultiple || !duplicateImport)) { + importVisitor.recursionDetector[fullPath] = true; + new ImportVisitor(importVisitor._importer, subFinish, context, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector) + .run(root); + return; + } + } + + subFinish(); + }); + } + } + visitArgs.visitDeeper = false; + return importNode; + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + return ruleNode; + }, + visitDirective: function (directiveNode, visitArgs) { + this.context.frames.unshift(directiveNode); + return directiveNode; + }, + visitDirectiveOut: function (directiveNode) { + this.context.frames.shift(); + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + this.context.frames.unshift(mixinDefinitionNode); + return mixinDefinitionNode; + }, + visitMixinDefinitionOut: function (mixinDefinitionNode) { + this.context.frames.shift(); + }, + visitRuleset: function (rulesetNode, visitArgs) { + this.context.frames.unshift(rulesetNode); + return rulesetNode; + }, + visitRulesetOut: function (rulesetNode) { + this.context.frames.shift(); + }, + visitMedia: function (mediaNode, visitArgs) { + this.context.frames.unshift(mediaNode.rules[0]); + return mediaNode; + }, + visitMediaOut: function (mediaNode) { + this.context.frames.shift(); + } +}; +module.exports = ImportVisitor; + +},{"../contexts":8,"./visitor":83}],80:[function(require,module,exports){ +var visitors = { + Visitor: require("./visitor"), + ImportVisitor: require('./import-visitor'), + ExtendVisitor: require('./extend-visitor'), + JoinSelectorVisitor: require('./join-selector-visitor'), + ToCSSVisitor: require('./to-css-visitor') +}; + +module.exports = visitors; + +},{"./extend-visitor":78,"./import-visitor":79,"./join-selector-visitor":81,"./to-css-visitor":82,"./visitor":83}],81:[function(require,module,exports){ +var Visitor = require("./visitor"); + +var JoinSelectorVisitor = function() { + this.contexts = [[]]; + this._visitor = new Visitor(this); +}; + +JoinSelectorVisitor.prototype = { + run: function (root) { + return this._visitor.visit(root); + }, + visitRule: function (ruleNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { + visitArgs.visitDeeper = false; + }, + + visitRuleset: function (rulesetNode, visitArgs) { + var context = this.contexts[this.contexts.length - 1], + paths = [], selectors; + + this.contexts.push(paths); + + if (! rulesetNode.root) { + selectors = rulesetNode.selectors; + if (selectors) { + selectors = selectors.filter(function(selector) { return selector.getIsOutput(); }); + rulesetNode.selectors = selectors.length ? selectors : (selectors = null); + if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); } + } + if (!selectors) { rulesetNode.rules = null; } + rulesetNode.paths = paths; + } + }, + visitRulesetOut: function (rulesetNode) { + this.contexts.length = this.contexts.length - 1; + }, + visitMedia: function (mediaNode, visitArgs) { + var context = this.contexts[this.contexts.length - 1]; + mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia); + } +}; + +module.exports = JoinSelectorVisitor; + +},{"./visitor":83}],82:[function(require,module,exports){ +var tree = require("../tree"), + Visitor = require("./visitor"); + +var ToCSSVisitor = function(context) { + this._visitor = new Visitor(this); + this._context = context; +}; + +ToCSSVisitor.prototype = { + isReplacing: true, + run: function (root) { + return this._visitor.visit(root); + }, + + visitRule: function (ruleNode, visitArgs) { + if (ruleNode.variable) { + return []; + } + return ruleNode; + }, + + visitMixinDefinition: function (mixinNode, visitArgs) { + // 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 = []; + return []; + }, + + visitExtend: function (extendNode, visitArgs) { + return []; + }, + + visitComment: function (commentNode, visitArgs) { + if (commentNode.isSilent(this._context)) { + return []; + } + return commentNode; + }, + + visitMedia: function(mediaNode, visitArgs) { + mediaNode.accept(this._visitor); + visitArgs.visitDeeper = false; + + if (!mediaNode.rules.length) { + return []; + } + return mediaNode; + }, + + visitDirective: function(directiveNode, visitArgs) { + if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) { + return []; + } + 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 (this.charset) { + if (directiveNode.debugInfo) { + var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "")+" */\n"); + comment.debugInfo = directiveNode.debugInfo; + return this._visitor.visit(comment); + } + return []; + } + this.charset = true; + } + if (directiveNode.rules && directiveNode.rules.rules) { + this._mergeRules(directiveNode.rules.rules); + } + return directiveNode; + }, + + checkPropertiesInRoot: function(rules) { + var ruleNode; + for(var i = 0; i < rules.length; i++) { + ruleNode = rules[i]; + if (ruleNode instanceof tree.Rule && !ruleNode.variable) { + throw { message: "properties must be inside selector blocks, they cannot be in the root.", + index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null}; + } + } + }, + + visitRuleset: function (rulesetNode, visitArgs) { + var rule, rulesets = []; + if (rulesetNode.firstRoot) { + this.checkPropertiesInRoot(rulesetNode.rules); + } + if (! rulesetNode.root) { + if (rulesetNode.paths) { + rulesetNode.paths = rulesetNode.paths + .filter(function(p) { + var i; + if (p[0].elements[0].combinator.value === ' ') { + p[0].elements[0].combinator = new(tree.Combinator)(''); + } + for(i = 0; i < p.length; i++) { + if (p[i].getIsReferenced() && p[i].getIsOutput()) { + return true; + } + } + return false; + }); + } + + // Compile rules and rulesets + var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0; + for (var i = 0; i < nodeRuleCnt; ) { + rule = nodeRules[i]; + if (rule && rule.rules) { + // visit because we are moving them out from being a child + rulesets.push(this._visitor.visit(rule)); + nodeRules.splice(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._visitor); + } else { + rulesetNode.rules = null; + } + visitArgs.visitDeeper = false; + + nodeRules = rulesetNode.rules; + if (nodeRules) { + this._mergeRules(nodeRules); + nodeRules = rulesetNode.rules; + } + if (nodeRules) { + this._removeDuplicateRules(nodeRules); + nodeRules = rulesetNode.rules; + } + + // now decide whether we keep the ruleset + if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) { + rulesets.splice(0, 0, rulesetNode); + } + } else { + rulesetNode.accept(this._visitor); + visitArgs.visitDeeper = false; + if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) { + rulesets.splice(0, 0, rulesetNode); + } + } + if (rulesets.length === 1) { + return rulesets[0]; + } + return rulesets; + }, + + _removeDuplicateRules: function(rules) { + if (!rules) { return; } + + // remove duplicates + var ruleCache = {}, + ruleList, rule, i; + + for(i = rules.length - 1; i >= 0 ; i--) { + rule = rules[i]; + if (rule instanceof tree.Rule) { + if (!ruleCache[rule.name]) { + ruleCache[rule.name] = rule; + } else { + ruleList = ruleCache[rule.name]; + if (ruleList instanceof tree.Rule) { + ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)]; + } + var ruleCSS = rule.toCSS(this._context); + if (ruleList.indexOf(ruleCSS) !== -1) { + rules.splice(i, 1); + } else { + ruleList.push(ruleCSS); + } + } + } + } + }, + + _mergeRules: function (rules) { + if (!rules) { return; } + + var groups = {}, + parts, + rule, + key; + + for (var i = 0; i < rules.length; i++) { + rule = rules[i]; + + if ((rule instanceof tree.Rule) && rule.merge) { + key = [rule.name, + rule.important ? "!" : ""].join(","); + + if (!groups[key]) { + groups[key] = []; + } else { + rules.splice(i--, 1); + } + + groups[key].push(rule); + } + } + + Object.keys(groups).map(function (k) { + + function toExpression(values) { + return new (tree.Expression)(values.map(function (p) { + return p.value; + })); + } + + function toValue(values) { + return new (tree.Value)(values.map(function (p) { + return p; + })); + } + + parts = groups[k]; + + if (parts.length > 1) { + rule = parts[0]; + var spacedGroups = []; + var lastSpacedGroup = []; + parts.map(function (p) { + if (p.merge==="+") { + if (lastSpacedGroup.length > 0) { + spacedGroups.push(toExpression(lastSpacedGroup)); + } + lastSpacedGroup = []; + } + lastSpacedGroup.push(p); + }); + spacedGroups.push(toExpression(lastSpacedGroup)); + rule.value = toValue(spacedGroups); + } + }); + } +}; + +module.exports = ToCSSVisitor; + +},{"../tree":56,"./visitor":83}],83:[function(require,module,exports){ +var tree = require("../tree"); + +var _visitArgs = { visitDeeper: true }, + _hasIndexed = false; + +function _noop(node) { + return node; +} + +function indexNodeTypes(parent, ticker) { + // add .typeIndex to tree node types for lookup table + var key, child; + for (key in parent) { + if (parent.hasOwnProperty(key)) { + child = parent[key]; + switch (typeof child) { + case "function": + // ignore bound functions directly on tree which do not have a prototype + // or aren't nodes + if (child.prototype && child.prototype.type) { + child.prototype.typeIndex = ticker++; + } + break; + case "object": + ticker = indexNodeTypes(child, ticker); + break; + } + } + } + return ticker; +} + +var Visitor = function(implementation) { + this._implementation = implementation; + this._visitFnCache = []; + + if (!_hasIndexed) { + indexNodeTypes(tree, 1); + _hasIndexed = true; + } +}; + +Visitor.prototype = { + visit: function(node) { + if (!node) { + return node; + } + + var nodeTypeIndex = node.typeIndex; + if (!nodeTypeIndex) { + return node; + } + + var visitFnCache = this._visitFnCache, + impl = this._implementation, + aryIndx = nodeTypeIndex << 1, + outAryIndex = aryIndx | 1, + func = visitFnCache[aryIndx], + funcOut = visitFnCache[outAryIndex], + visitArgs = _visitArgs, + fnName; + + visitArgs.visitDeeper = true; + + if (!func) { + fnName = "visit" + node.type; + func = impl[fnName] || _noop; + funcOut = impl[fnName + "Out"] || _noop; + visitFnCache[aryIndx] = func; + visitFnCache[outAryIndex] = funcOut; + } + + if (func !== _noop) { + var newNode = func.call(impl, node, visitArgs); + if (impl.isReplacing) { + node = newNode; + } + } + + if (visitArgs.visitDeeper && node && node.accept) { + node.accept(this); + } + + if (funcOut != _noop) { + funcOut.call(impl, node); + } + + return node; + }, + visitArray: function(nodes, nonReplacing) { + if (!nodes) { + return nodes; + } + + var cnt = nodes.length, i; + + // Non-replacing + if (nonReplacing || !this._implementation.isReplacing) { + for (i = 0; i < cnt; i++) { + this.visit(nodes[i]); + } + return nodes; + } + + // Replacing + var out = []; + for (i = 0; i < cnt; i++) { + var evald = this.visit(nodes[i]); + if (!evald.splice) { + out.push(evald); + } else if (evald.length) { + this.flatten(evald, out); + } + } + return out; + }, + flatten: function(arr, out) { + if (!out) { + out = []; + } + + var cnt, i, item, + nestedCnt, j, nestedItem; + + for (i = 0, cnt = arr.length; i < cnt; i++) { + item = arr[i]; + if (!item.splice) { + out.push(item); + continue; + } + + for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) { + nestedItem = item[j]; + if (!nestedItem.splice) { + out.push(nestedItem); + } else if (nestedItem.length) { + this.flatten(nestedItem, out); + } + } + } + + return out; + } +}; +module.exports = Visitor; + +},{"../tree":56}],84:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; + +process.nextTick = (function () { + var canSetImmediate = typeof window !== 'undefined' + && window.setImmediate; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canSetImmediate) { + return function (f) { return window.setImmediate(f) }; + } + + if (canPost) { + var queue = []; + window.addEventListener('message', function (ev) { + var source = ev.source; + if ((source === window || source === null) && ev.data === 'process-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +} + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +},{}],85:[function(require,module,exports){ +'use strict'; + +var asap = require('asap') + +module.exports = Promise; +function Promise(fn) { + if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new') + if (typeof fn !== 'function') throw new TypeError('not a function') + var state = null + var value = null + var deferreds = [] + var self = this + + this.then = function(onFulfilled, onRejected) { + return new self.constructor(function(resolve, reject) { + handle(new Handler(onFulfilled, onRejected, resolve, reject)) + }) + } + + function handle(deferred) { + if (state === null) { + deferreds.push(deferred) + return + } + asap(function() { + var cb = state ? deferred.onFulfilled : deferred.onRejected + if (cb === null) { + (state ? deferred.resolve : deferred.reject)(value) + return + } + var ret + try { + ret = cb(value) + } + catch (e) { + deferred.reject(e) + return + } + deferred.resolve(ret) + }) + } + + function resolve(newValue) { + try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.') + if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { + var then = newValue.then + if (typeof then === 'function') { + doResolve(then.bind(newValue), resolve, reject) + return + } + } + state = true + value = newValue + finale() + } catch (e) { reject(e) } + } + + function reject(newValue) { + state = false + value = newValue + finale() + } + + function finale() { + for (var i = 0, len = deferreds.length; i < len; i++) + handle(deferreds[i]) + deferreds = null + } + + doResolve(fn, resolve, reject) +} + + +function Handler(onFulfilled, onRejected, resolve, reject){ + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null + this.onRejected = typeof onRejected === 'function' ? onRejected : null + this.resolve = resolve + this.reject = reject +} + +/** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ +function doResolve(fn, onFulfilled, onRejected) { + var done = false; + try { + fn(function (value) { + if (done) return + done = true + onFulfilled(value) + }, function (reason) { + if (done) return + done = true + onRejected(reason) + }) + } catch (ex) { + if (done) return + done = true + onRejected(ex) + } +} + +},{"asap":87}],86:[function(require,module,exports){ +'use strict'; + +//This file contains the ES6 extensions to the core Promises/A+ API + +var Promise = require('./core.js') +var asap = require('asap') + +module.exports = Promise + +/* Static Functions */ + +function ValuePromise(value) { + this.then = function (onFulfilled) { + if (typeof onFulfilled !== 'function') return this + return new Promise(function (resolve, reject) { + asap(function () { + try { + resolve(onFulfilled(value)) + } catch (ex) { + reject(ex); + } + }) + }) + } +} +ValuePromise.prototype = Promise.prototype + +var TRUE = new ValuePromise(true) +var FALSE = new ValuePromise(false) +var NULL = new ValuePromise(null) +var UNDEFINED = new ValuePromise(undefined) +var ZERO = new ValuePromise(0) +var EMPTYSTRING = new ValuePromise('') + +Promise.resolve = function (value) { + if (value instanceof Promise) return value + + if (value === null) return NULL + if (value === undefined) return UNDEFINED + if (value === true) return TRUE + if (value === false) return FALSE + if (value === 0) return ZERO + if (value === '') return EMPTYSTRING + + if (typeof value === 'object' || typeof value === 'function') { + try { + var then = value.then + if (typeof then === 'function') { + return new Promise(then.bind(value)) + } + } catch (ex) { + return new Promise(function (resolve, reject) { + reject(ex) + }) + } + } + + return new ValuePromise(value) +} + +Promise.all = function (arr) { + var args = Array.prototype.slice.call(arr) + + return new Promise(function (resolve, reject) { + if (args.length === 0) return resolve([]) + var remaining = args.length + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then + if (typeof then === 'function') { + then.call(val, function (val) { res(i, val) }, reject) + return + } + } + args[i] = val + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex) + } + } + for (var i = 0; i < args.length; i++) { + res(i, args[i]) + } + }) +} + +Promise.reject = function (value) { + return new Promise(function (resolve, reject) { + reject(value); + }); +} + +Promise.race = function (values) { + return new Promise(function (resolve, reject) { + values.forEach(function(value){ + Promise.resolve(value).then(resolve, reject); + }) + }); +} + +/* Prototype Methods */ + +Promise.prototype['catch'] = function (onRejected) { + return this.then(null, onRejected); +} + +},{"./core.js":85,"asap":87}],87:[function(require,module,exports){ +(function (process){ + +// Use the fastest possible means to execute a task in a future turn +// of the event loop. + +// linked list of tasks (single, with head node) +var head = {task: void 0, next: null}; +var tail = head; +var flushing = false; +var requestFlush = void 0; +var isNodeJS = false; + +function flush() { + /* jshint loopfunc: true */ + + while (head.next) { + head = head.next; + var task = head.task; + head.task = void 0; + var domain = head.domain; + + if (domain) { + head.domain = void 0; + domain.enter(); + } + + try { + task(); + + } catch (e) { + if (isNodeJS) { + // In node, uncaught exceptions are considered fatal errors. + // Re-throw them synchronously to interrupt flushing! + + // Ensure continuation if the uncaught exception is suppressed + // listening "uncaughtException" events (as domains does). + // Continue in next event to avoid tick recursion. + if (domain) { + domain.exit(); + } + setTimeout(flush, 0); + if (domain) { + domain.enter(); + } + + throw e; + + } else { + // In browsers, uncaught exceptions are not fatal. + // Re-throw them asynchronously to avoid slow-downs. + setTimeout(function() { + throw e; + }, 0); + } + } + + if (domain) { + domain.exit(); + } + } + + flushing = false; +} + +if (typeof process !== "undefined" && process.nextTick) { + // Node.js before 0.9. Note that some fake-Node environments, like the + // Mocha test runner, introduce a `process` global without a `nextTick`. + isNodeJS = true; + + requestFlush = function () { + process.nextTick(flush); + }; + +} else if (typeof setImmediate === "function") { + // In IE10, Node.js 0.9+, or https://github.com/NobleJS/setImmediate + if (typeof window !== "undefined") { + requestFlush = setImmediate.bind(window, flush); + } else { + requestFlush = function () { + setImmediate(flush); + }; + } + +} else if (typeof MessageChannel !== "undefined") { + // modern browsers + // http://www.nonblocking.io/2011/06/windownexttick.html + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + requestFlush = function () { + channel.port2.postMessage(0); + }; + +} else { + // old browsers + requestFlush = function () { + setTimeout(flush, 0); + }; +} + +function asap(task) { + tail = tail.next = { + task: task, + domain: isNodeJS && process.domain, + next: null + }; + + if (!flushing) { + flushing = true; + requestFlush(); + } +}; + +module.exports = asap; + + +}).call(this,require('_process')) +},{"_process":84}],88:[function(require,module,exports){ +// should work in any browser without browserify + +if (typeof Promise.prototype.done !== 'function') { + Promise.prototype.done = function (onFulfilled, onRejected) { + var self = arguments.length ? this.then.apply(this, arguments) : this + self.then(null, function (err) { + setTimeout(function () { + throw err + }, 0) + }) + } +} +},{}],"promise/polyfill.js":[function(require,module,exports){ +// not "use strict" so we can declare global "Promise" + +var asap = require('asap'); + +if (typeof Promise === 'undefined') { + Promise = require('./lib/core.js') + require('./lib/es6-extensions.js') +} + +require('./polyfill-done.js'); + +},{"./lib/core.js":85,"./lib/es6-extensions.js":86,"./polyfill-done.js":88,"asap":87}]},{},[5]); diff --git a/dist/less.min.js b/dist/less.min.js new file mode 100644 index 000000000..2ff7e8a9d --- /dev/null +++ b/dist/less.min.js @@ -0,0 +1,16 @@ +/*! + * Less - Leaner CSS v2.0.0-b1 + * http://lesscss.org + * + * Copyright (c) 2009-2014, Alexis Sellier + * Licensed under the Apache v2 License. + * + */ + + /** * @license Apache v2 + */ + +require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g0&&i.childNodes.length>0&&g.firstChild.nodeValue===i.firstChild.nodeValue);var j=a.getElementsByTagName("head")[0];if(null===g||h===!1){var k=d&&d.nextSibling||null;k?k.parentNode.insertBefore(i,k):j.appendChild(i)}if(g&&h===!1&&g.parentNode.removeChild(g),i.styleSheet)try{i.styleSheet.cssText=b}catch(l){throw new Error("Couldn't reassign styleSheet.cssText.")}}}},{"./utils":7}],2:[function(a,b){b.exports=function(a,b,c){var d=null;if("development"!==b.env)try{d="undefined"==typeof a.localStorage?null:a.localStorage}catch(e){}return{setCSS:function(a,b,e){if(d){c.info("saving "+a+" to cache.");try{d.setItem(a,e),d.setItem(a+":timestamp",b)}catch(f){c.error("failed to save")}}},getCSS:function(a,b){var c=d&&d.getItem(a),e=d&&d.getItem(a+":timestamp");return e&&b.lastModified&&new Date(b.lastModified).valueOf()===new Date(e).valueOf()?c:void 0}}}},{}],3:[function(a,b){var c=a("./utils"),d=a("./browser");b.exports=function(a,b,e){function f(b,f){var g,h,i="less-error-message:"+c.extractId(f||""),j='
  • {content}
  • ',k=a.document.createElement("div"),l=[],m=b.filename||f,n=m.match(/([^\/]+(\?.*)?)$/)[1];k.id=i,k.className="less-error-message",h="

    "+(b.type||"Syntax")+"Error: "+(b.message||"There is an error in your .less file")+'

    in '+n+" ";var o=function(a,b,c){void 0!==a.extract[b]&&l.push(j.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};b.extract&&(o(b,0,""),o(b,1,"line"),o(b,2,""),h+="on line "+b.line+", column "+(b.column+1)+":

      "+l.join("")+"
    "),b.stack&&(b.extract||e.logLevel>=4)&&(h+="
    Stack Trace
    "+b.stack.split("\n").slice(1).join("
    ")),k.innerHTML=h,d.createCSS(a.document,[".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),k.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),"development"===e.env&&(g=setInterval(function(){var b=a.document,c=b.body;c&&(b.getElementById(i)?c.replaceChild(k,b.getElementById(i)):c.insertBefore(k,c.firstChild),clearInterval(g))},10))}function g(a,b){e.errorReporting&&"html"!==e.errorReporting?"console"===e.errorReporting?k(a,b):"function"==typeof e.errorReporting&&e.errorReporting("add",a,b):f(a,b)}function h(b){var d=a.document.getElementById("less-error-message:"+c.extractId(b));d&&d.parentNode.removeChild(d)}function i(){}function j(a){e.errorReporting&&"html"!==e.errorReporting?"console"===e.errorReporting?i(a):"function"==typeof e.errorReporting&&e.errorReporting("remove",a):h(a)}function k(a,c){var d="{line} {content}",f=a.filename||c,g=[],h=(a.type||"Syntax")+"Error: "+(a.message||"There is an error in your .less file")+" in "+f+" ",i=function(a,b,c){void 0!==a.extract[b]&&g.push(d.replace(/\{line\}/,(parseInt(a.line,10)||0)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.extract&&(i(a,0,""),i(a,1,"line"),i(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":\n"+g.join("\n")),a.stack&&(a.extract||e.logLevel>=4)&&(h+="\nStack Trace\n"+a.stack),b.logger.error(h)}return{add:g,remove:j}}},{"./browser":1,"./utils":7}],4:[function(a,b){b.exports=function(b,c,d){function e(){if(window.XMLHttpRequest&&!("file:"===window.location.protocol&&"ActiveXObject"in window))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(a){return d.error("browser doesn't support AJAX."),null}}var f="undefined"==typeof Promise?a("promise"):Promise,g=a("../less/environment/abstract-file-manager.js"),h={},i=function(){};return i.prototype=new g,i.prototype.alwaysMakePathsAbsolute=function(){return!0},i.prototype.join=function(a,b){return a?this.extractUrlParts(b,a).path:b},i.prototype.doXHR=function(a,f,g,h){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):"function"==typeof d&&d(b.status,a)}var j=e(),k=c?b.fileAsync:b.async;"function"==typeof j.overrideMimeType&&j.overrideMimeType("text/css"),d.debug("XHR: Getting '"+a+"'"),j.open("GET",a,k),j.setRequestHeader("Accept",f||"text/x-less, text/css; q=0.9, */*; q=0.5"),j.send(null),c&&!b.fileAsync?0===j.status||j.status>=200&&j.status<300?g(j.responseText):h(j.status,a):k?j.onreadystatechange=function(){4==j.readyState&&i(j,g,h)}:i(j,g,h)},i.prototype.supports=function(){return!0},i.prototype.loadFile=function(a,b,c){return new f(function(d,e){b&&!this.isPathAbsolute(a)&&(a=b+a),c=c||{};var f=this.extractUrlParts(a,window.location.href),g=f.url;if(c.useFileCache&&h[g])try{var i=h[g];d({contents:i,filename:g,webInfo:{lastModified:new Date}})}catch(j){e({filename:g,message:"Error loading file "+g+" error was "+j.message})}else this.doXHR(g,c.mime,function(a,b){h[g]=a,d({contents:a,filename:g,webInfo:{lastModified:b}})},function(a,b){e({type:"File",message:"'"+b+"' wasn't found ("+a+")",href:g})})}.bind(this))},i}},{"../less/environment/abstract-file-manager.js":12,promise:void 0}],5:[function(a){function b(a){return k.postProcessor&&"function"==typeof k.postProcessor&&(a=k.postProcessor.call(a,a)||a),a}function c(a){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b}function d(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){var d=c.concat(Array.prototype.slice.call(arguments,0));return a.apply(b,d)}}function e(a){for(var b,e=document.getElementsByTagName("style"),f=0;f0||j?"development":"production"),k.async=k.async||!1,k.fileAsync=k.fileAsync||!1,k.poll=k.poll||(j?1e3:1500),k.functions&&i.functions.functionRegistry.addMultiple(k.functions);var r=/!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);r&&(k.dumpLineNumbers=r[1]);var s=/^text\/(x-)?less$/;i.watch=function(){return i.watchMode||(i.env="development",h()),this.watchMode=!0,!0},i.unwatch=function(){return clearInterval(i.watchTimer),this.watchMode=!1,!1},/!watch/.test(location.hash)&&i.watch(),i.registerStylesheets=function(){return new Promise(function(a){var b=document.getElementsByTagName("link");i.sheets=[];for(var c=0;c=c&&console.log(a)},info:function(a){b.logLevel>=d&&console.log(a)},warn:function(a){b.logLevel>=e&&console.warn(a)},error:function(a){b.logLevel>=f&&console.error(a)}}]);for(var g=0;gb&&(b=a.lastIndexOf("\\")),0>b?"":a.slice(0,b+1)},c.prototype.supportsSync=function(){return!1},c.prototype.alwaysMakePathsAbsolute=function(){return!1},c.prototype.isPathAbsolute=function(a){return/^(?:[a-z-]+:|\/|\\)/i.test(a)},c.prototype.join=function(a,b){return a?a+b:b},c.prototype.pathDiff=function(a,b){var c,d,e,f,g=this.extractUrlParts(a),h=this.extractUrlParts(b),i="";if(g.hostPart!==h.hostPart)return"";for(d=Math.max(h.directories.length,g.directories.length),c=0;d>c&&h.directories[c]===g.directories[c];c++);for(f=h.directories.slice(c),e=g.directories.slice(c),c=0;c0&&(h.splice(c-1,2),c-=2)}return g.hostPart=f[1],g.directories=h,g.path=(f[1]||"")+h.join("/"),g.fileUrl=g.path+(f[4]||""),g.url=g.fileUrl+(f[5]||""),g},b.exports=c},{}],13:[function(a,b){var c=function(a,b){this.fileManagers=b||[],a=a||{};for(var c=["encodeBase64","mimeLookup","charsetLookup","getSourceMapGenerator"],d=[],e=d.concat(c),f=0;f=0;g--){var h=f[g];if(h[e?"supportsSync":"supports"](a,b,c,d))return h}return null},c.prototype.addFileManager=function(a){this.fileManagers.push(a)},c.prototype.clearFileManagers=function(){this.fileManagers=[]},b.exports=c},{}],14:[function(a){function b(a,b,d){var e,f,g,h,i=b.alpha,j=d.alpha,k=[];g=j+i*(1-j);for(var l=0;3>l;l++)e=b.rgb[l]/255,f=d.rgb[l]/255,h=a(e,f),g&&(h=(j*f+i*(e-j*(e+f-h)))/g),k[l]=255*h;return new c(k,g)}var c=a("../tree/color"),d=a("./function-registry"),e={multiply:function(a,b){return a*b},screen:function(a,b){return a+b-a*b},overlay:function(a,b){return a*=2,1>=a?e.multiply(a,b):e.screen(a-1,b)},softlight:function(a,b){var c=1,d=a;return b>.5&&(d=1,c=a>.25?Math.sqrt(a):((16*a-12)*a+4)*a),a-(1-2*b)*d*(c-a)},hardlight:function(a,b){return e.overlay(b,a)},difference:function(a,b){return Math.abs(a-b)},exclusion:function(a,b){return a+b-2*a*b},average:function(a,b){return(a+b)/2},negation:function(a,b){return 1-Math.abs(a+b-1)}};for(var f in e)e.hasOwnProperty(f)&&(b[f]=b.bind(null,e[f]));d.addMultiple(b)},{"../tree/color":44,"./function-registry":19}],15:[function(a){function b(a){return Math.min(1,Math.max(0,a))}function c(a){return f.hsla(a.h,a.s,a.l,a.a)}function d(a){if(a instanceof g)return parseFloat(a.unit.is("%")?a.value/100:a.value);if("number"==typeof a)return a;throw{type:"Argument",message:"color functions take numbers as parameters"}}function e(a,b){return a instanceof g&&a.unit.is("%")?parseFloat(a.value*b/100):d(a)}var f,g=a("../tree/dimension"),h=a("../tree/color"),i=a("../tree/quoted"),j=a("../tree/anonymous"),k=a("./function-registry");f={rgb:function(a,b,c){return f.rgba(a,b,c,1)},rgba:function(a,b,c,f){var g=[a,b,c].map(function(a){return e(a,255)});return f=d(f),new h(g,f)},hsl:function(a,b,c){return f.hsla(a,b,c,1)},hsla:function(a,c,e,g){function h(a){return a=0>a?a+1:a>1?a-1:a,1>6*a?j+(i-j)*a*6:1>2*a?i:2>3*a?j+(i-j)*(2/3-a)*6:j}a=d(a)%360/360,c=b(d(c)),e=b(d(e)),g=b(d(g));var i=.5>=e?e*(c+1):e+c-e*c,j=2*e-i;return f.rgba(255*h(a+1/3),255*h(a),255*h(a-1/3),g)},hsv:function(a,b,c){return f.hsva(a,b,c,1)},hsva:function(a,b,c,e){a=d(a)%360/360*360,b=d(b),c=d(c),e=d(e);var g,h;g=Math.floor(a/60%6),h=a/60-g;var i=[c,c*(1-b),c*(1-h*b),c*(1-(1-h)*b)],j=[[0,3,1],[2,0,1],[1,0,3],[1,2,0],[3,1,0],[0,1,2]];return f.rgba(255*i[j[g][0]],255*i[j[g][1]],255*i[j[g][2]],e)},hue:function(a){return new g(a.toHSL().h)},saturation:function(a){return new g(100*a.toHSL().s,"%")},lightness:function(a){return new g(100*a.toHSL().l,"%")},hsvhue:function(a){return new g(a.toHSV().h)},hsvsaturation:function(a){return new g(100*a.toHSV().s,"%")},hsvvalue:function(a){return new g(100*a.toHSV().v,"%")},red:function(a){return new g(a.rgb[0])},green:function(a){return new g(a.rgb[1])},blue:function(a){return new g(a.rgb[2])},alpha:function(a){return new g(a.toHSL().a)},luma:function(a){return new g(a.luma()*a.alpha*100,"%")},luminance:function(a){var b=.2126*a.rgb[0]/255+.7152*a.rgb[1]/255+.0722*a.rgb[2]/255;return new g(b*a.alpha*100,"%")},saturate:function(a,d){if(!a.rgb)return null;var e=a.toHSL();return e.s+=d.value/100,e.s=b(e.s),c(e)},desaturate:function(a,d){var e=a.toHSL();return e.s-=d.value/100,e.s=b(e.s),c(e)},lighten:function(a,d){var e=a.toHSL();return e.l+=d.value/100,e.l=b(e.l),c(e)},darken:function(a,d){var e=a.toHSL();return e.l-=d.value/100,e.l=b(e.l),c(e)},fadein:function(a,d){var e=a.toHSL();return e.a+=d.value/100,e.a=b(e.a),c(e)},fadeout:function(a,d){var e=a.toHSL();return e.a-=d.value/100,e.a=b(e.a),c(e)},fade:function(a,d){var e=a.toHSL();return e.a=d.value/100,e.a=b(e.a),c(e)},spin:function(a,b){var d=a.toHSL(),e=(d.h+b.value)%360;return d.h=0>e?360+e:e,c(d)},mix:function(a,b,c){c||(c=new g(50));var d=c.value/100,e=2*d-1,f=a.toHSL().a-b.toHSL().a,i=((e*f==-1?e:(e+f)/(1+e*f))+1)/2,j=1-i,k=[a.rgb[0]*i+b.rgb[0]*j,a.rgb[1]*i+b.rgb[1]*j,a.rgb[2]*i+b.rgb[2]*j],l=a.alpha*d+b.alpha*(1-d);return new h(k,l)},greyscale:function(a){return f.desaturate(a,new g(100))},contrast:function(a,b,c,e){if(!a.rgb)return null;if("undefined"==typeof c&&(c=f.rgba(255,255,255,1)),"undefined"==typeof b&&(b=f.rgba(0,0,0,1)),b.luma()>c.luma()){var g=c;c=b,b=g}return e="undefined"==typeof e?.43:d(e),a.luma()=r&&this.context.ieCompat!==!1)return g.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!",i,s,r),f(this,e||a);q=k?q.toString("base64"):encodeURIComponent(q);var t='"data:'+h+","+q+m+'"';return new d(new c(t),this.index,this.currentFileInfo)})}},{"../logger":29,"../tree/anonymous":40,"../tree/url":74,"./function-registry":19}],17:[function(a,b){var c=a("../tree/keyword"),d=a("./function-registry"),e={eval:function(){var a=this.value_,b=this.error_;if(b)throw b;return null!=a?a?c.True:c.False:void 0},value:function(a){this.value_=a},error:function(a){this.error_=a},reset:function(){this.value_=this.error_=null}};d.add("default",e.eval.bind(e)),b.exports=e},{"../tree/keyword":59,"./function-registry":19}],18:[function(a,b){var c=a("./function-registry"),d=function(a,b,d,e){this.name=a.toLowerCase(),this.function=c.get(this.name),this.index=d,this.context=b,this.currentFileInfo=e};d.prototype.isValid=function(){return Boolean(this.function)},d.prototype.call=function(a){return this.function.apply(this,a)},b.exports=d},{"./function-registry":19}],19:[function(a,b){b.exports={_data:{},add:function(a,b){this._data.hasOwnProperty(a),this._data[a]=b},addMultiple:function(a){Object.keys(a).forEach(function(b){this.add(b,a[b])}.bind(this))},get:function(a){return this._data[a]}}},{}],20:[function(a,b){b.exports=function(b){var c={functionRegistry:a("./function-registry"),functionCaller:a("./function-caller")};return a("./default"),a("./color"),a("./color-blending"),a("./data-uri")(b),a("./math"),a("./number"),a("./string"),a("./svg")(b),a("./types"),c}},{"./color":15,"./color-blending":14,"./data-uri":16,"./default":17,"./function-caller":18,"./function-registry":19,"./math":21,"./number":22,"./string":23,"./svg":24,"./types":25}],21:[function(a){function b(a,b,d){if(!(d instanceof c))throw{type:"Argument",message:"argument must be a number"};return null==b?b=d.unit:d=d.unify(),new c(a(parseFloat(d.value)),b)}var c=a("../tree/dimension"),d=a("./function-registry"),e={ceil:null,floor:null,sqrt:null,abs:null,tan:"",sin:"",cos:"",atan:"rad",asin:"rad",acos:"rad"};for(var f in e)e.hasOwnProperty(f)&&(e[f]=b.bind(null,Math[f],e[f]));e.round=function(a,c){var d="undefined"==typeof c?0:c.value;return b(function(a){return a.toFixed(d)},null,a)},d.addMultiple(e)},{"../tree/dimension":50,"./function-registry":19}],22:[function(a){var b=a("../tree/dimension"),c=a("../tree/anonymous"),d=a("./function-registry"),e=function(a,d){switch(d=Array.prototype.slice.call(d),d.length){case 0:throw{type:"Argument",message:"one or more arguments required"}}var e,f,g,h,i,j,k,l,m=[],n={};for(e=0;ei.value)&&(m[f]=g);else{if(void 0!==k&&j!==k)throw{type:"Argument",message:"incompatible types"};n[j]=m.length,m.push(g)}else Array.isArray(d[e].value)&&Array.prototype.push.apply(d,Array.prototype.slice.call(d[e].value));return 1==m.length?m[0]:(d=m.map(function(a){return a.toCSS(this.context)}).join(this.context.compress?",":", "),new c((a?"min":"max")+"("+d+")"))};d.addMultiple({min:function(){return e(!0,arguments)},max:function(){return e(!1,arguments)},convert:function(a,b){return a.convertTo(b.value)},pi:function(){return new b(Math.PI)},mod:function(a,c){return new b(a.value%c.value,a.unit)},pow:function(a,c){if("number"==typeof a&&"number"==typeof c)a=new b(a),c=new b(c);else if(!(a instanceof b&&c instanceof b))throw{type:"Argument",message:"arguments must be numbers"};return new b(Math.pow(a.value,c.value),a.unit)},percentage:function(a){return new b(100*a.value,"%")}})},{"../tree/anonymous":40,"../tree/dimension":50,"./function-registry":19}],23:[function(a){var b=a("../tree/quoted"),c=a("../tree/anonymous"),d=a("../tree/javascript"),e=a("./function-registry");e.addMultiple({e:function(a){return new c(a instanceof d?a.evaluated:a.value)},escape:function(a){return new c(encodeURI(a.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},replace:function(a,c,d,e){var f=a.value;return f=f.replace(new RegExp(c.value,e?e.value:""),d.value),new b(a.quote||"",f,a.escaped)},"%":function(a){for(var c=Array.prototype.slice.call(arguments,1),d=a.value,e=0;e",j=0;jn?' stop-opacity="'+n+'"':"")+"/>";if(i+="',r)try{i=b.encodeBase64(i)}catch(u){r=!1}return i="'data:image/svg+xml"+(r?";base64":"")+","+i+"'",new f(new e(i),this.index,this.currentFileInfo)})}},{"../tree/anonymous":40,"../tree/color":44,"../tree/dimension":50,"../tree/url":74,"./function-registry":19}],25:[function(a){var b=a("../tree/keyword"),c=a("../tree/dimension"),d=a("../tree/color"),e=a("../tree/quoted"),f=a("../tree/anonymous"),g=a("../tree/url"),h=a("../tree/operation"),i=a("./function-registry"),j=function(a,c){return a instanceof c?b.True:b.False},k=function(a,d){return a instanceof c&&a.unit.is(d.value||d)?b.True:b.False};i.addMultiple({iscolor:function(a){return j(a,d)},isnumber:function(a){return j(a,c)},isstring:function(a){return j(a,e)},iskeyword:function(a){return j(a,b)},isurl:function(a){return j(a,g)},ispixel:function(a){return k(a,"px")},ispercentage:function(a){return k(a,"%")},isem:function(a){return k(a,"em")},isunit:k,unit:function(a,d){if(!(a instanceof c))throw{type:"Argument",message:"the first argument to unit must be a number"+(a instanceof h?". Have you forgotten parenthesis?":"")};return d=d?d instanceof b?d.value:d.toCSS():"",new c(a.value,d)},"get-unit":function(a){return new f(a.unit)},extract:function(a,b){return b=b.value-1,Array.isArray(a.value)?a.value[b]:Array(a)[b]},length:function(a){var b=Array.isArray(a.value)?a.value.length:1;return new c(b)}})},{"../tree/anonymous":40,"../tree/color":44,"../tree/dimension":50,"../tree/keyword":59,"../tree/operation":65,"../tree/quoted":67,"../tree/url":74,"./function-registry":19}],26:[function(a,b){var c=a("./contexts"),d=a("./parser/parser");b.exports=function(a){var b=function(a,b){this.rootFilename=b.filename,this.paths=a.paths||[],this.contents={},this.contentsIgnoredChars={},this.mime=a.mime,this.error=null,this.context=a,this.queue=[],this.files=[]};return b.prototype.push=function(b,e,f,g){var h=this;this.queue.push(b);var i=function(a,c,d){h.queue.splice(h.queue.indexOf(b),1);var e=d===h.rootFilename;h.files[d]=c,a&&!h.error&&(h.error=a),g(a,c,e,d)},j={relativeUrls:this.context.relativeUrls,entryPath:e.entryPath,rootpath:e.rootpath,rootFilename:e.rootFilename},k=a.getFileManager(b,e.currentDirectory,this.context,a);return k?void k.loadFile(b,e.currentDirectory,this.context,a).then(function(a){var b=a.filename,g=a.contents; +j.currentDirectory=k.getPath(b),j.relativeUrls&&(j.rootpath=k.join(h.context.rootpath||"",k.pathDiff(j.currentDirectory,j.entryPath)),!k.isPathAbsolute(j.rootpath)&&k.alwaysMakePathsAbsolute()&&(j.rootpath=k.join(j.entryPath,j.rootpath))),j.filename=b;var l=new c.Parse(h.context);l.processImports=!1,h.contents[b]=g,(e.reference||f.reference)&&(j.reference=!0),f.inline?i(null,g,b):new d(l,h,j).parse(g,function(a,c){i(a,c,b)})},function(a){i(a)}):void i({message:"Could not find a file-manager for "+b})},b}},{"./contexts":8,"./parser/parser":33}],27:[function(a,b){b.exports=function(b,c){var d,e,f,g,h,i={version:[2,0,"0-b1"],data:a("./data"),tree:a("./tree"),Environment:h=a("./environment/environment"),AbstractFileManager:a("./environment/abstract-file-manager"),environment:b=new h(b,c),visitors:a("./visitors"),Parser:a("./parser/parser"),functions:a("./functions")(b),contexts:a("./contexts"),SourceMapOutput:d=a("./source-map-output")(b),SourceMapBuilder:e=a("./source-map-builder")(d),ParseTree:f=a("./parse-tree")(e),ImportManager:g=a("./import-manager")(b),render:a("./render")(b,f,g),LessError:a("./less-error"),transformTree:a("./transform-tree"),utils:a("./utils"),PluginManager:a("./plugin-manager"),logger:a("./logger")};return i}},{"./contexts":8,"./data":10,"./environment/abstract-file-manager":12,"./environment/environment":13,"./functions":20,"./import-manager":26,"./less-error":28,"./logger":29,"./parse-tree":30,"./parser/parser":33,"./plugin-manager":34,"./render":35,"./source-map-builder":36,"./source-map-output":37,"./transform-tree":38,"./tree":56,"./utils":77,"./visitors":80}],28:[function(a,b){var c=a("./utils"),d=b.exports=function(a,b,d){Error.call(this);var e=a.filename||d;if(b&&e){var f=b.contents[e],g=c.getLocation(a.index,f),h=g.line,i=g.column,j=a.call&&c.getLocation(a.call,f).line,k=f.split("\n");this.type=a.type||"Syntax",this.filename=e,this.index=a.index,this.line="number"==typeof h?h+1:null,this.callLine=j+1,this.callExtract=k[j],this.column=i,this.extract=[k[h-1],k[h],k[h+1]]}this.message=a.message,this.stack=a.stack};d.prototype=Object.create(Error.prototype),d.prototype.constructor=d},{"./utils":77}],29:[function(a,b){b.exports={error:function(a){this._fireEvent("error",a)},warn:function(a){this._fireEvent("warn",a)},info:function(a){this._fireEvent("info",a)},debug:function(a){this._fireEvent("debug",a)},addListener:function(a){this._listeners.push(a)},removeListener:function(a){for(var b=0;bc&&!b||!c||(p.push(a.slice(q,h+1)),q=h+1)}var d,e,f,g,h,i,j,k,l,m=a.length,n=0,o=0,p=[],q=0;for(h=0;m>h;h++)if(j=a.charCodeAt(h),!(j>=97&&122>=j||34>j))switch(j){case 40:o++,e=h;continue;case 41:if(--o<0)return b("missing opening `(`",h);continue;case 59:o||c();continue;case 123:n++,d=h;continue;case 125:if(--n<0)return b("missing opening `{`",h);n||o||c();continue;case 92:if(m-1>h){h++;continue}return b("unescaped `\\`",h);case 34:case 39:case 96:for(l=0,i=h,h+=1;m>h;h++)if(k=a.charCodeAt(h),!(k>96)){if(k==j){l=1;break}if(92==k){if(h==m-1)return b("unescaped `\\`",h);h++}}if(l)continue;return b("unmatched `"+String.fromCharCode(j)+"`",i);case 47:if(o||h==m-1)continue;if(k=a.charCodeAt(h+1),47==k)for(h+=2;m>h&&(k=a.charCodeAt(h),!(13>=k)||10!=k&&13!=k);h++);else if(42==k){for(f=i=h,h+=2;m-1>h&&(k=a.charCodeAt(h),125==k&&(g=h),42!=k||47!=a.charCodeAt(h+1));h++);if(h==m-1)return b("missing closing `*/`",i);h++}continue;case 42:if(m-1>h&&47==a.charCodeAt(h+1))return b("unmatched `/*`",h);continue}return 0!==n?f>d&&g>f?b("missing closing `}` or `*/`",d):b("missing closing `}`",d):0!==o?b("missing closing `)`",e):(c(!0),p)}},{}],32:[function(a,b){var c=a("./chunker");b.exports=function(){function a(){k.i>i&&(h=h.slice(k.i-i),i=k.i)}var b,d,e,f,g,h,i,j=[],k={};k.save=function(){i=k.i,j.push({current:h,i:k.i,j:d})},k.restore=function(a){k.i>e&&(e=k.i,f=a);var b=j.pop();h=b.current,i=k.i=b.i,d=b.j},k.forget=function(){j.pop()},k.isWhitespace=function(a){var c=k.i+(a||0),d=b.charCodeAt(c);return d===l||d===o||d===m||d===n},k.$=function(c){var d,e,f=typeof c;return"string"===f?b.charAt(k.i)!==c?null:(t(1),c):(a(),(d=c.exec(h))?(e=d[0].length,t(e),"string"==typeof d?d:1===d.length?d[0]:d):null)},k.$re=function(a){k.i>i&&(h=h.slice(k.i-i),i=k.i);var b=a.exec(h);return b?(t(b[0].length),"string"==typeof b?b:1===b.length?b[0]:b):null},k.$char=function(a){return b.charAt(k.i)!==a?null:(t(1),a)};var l=32,m=9,n=10,o=13,p=43,q=44,r=47,s=57;k.autoCommentAbsorb=!0,k.commentStore=[],k.finished=!1;var t=function(a){for(var c,e,f,j=k.i,p=d,q=k.i-i,s=k.i+h.length-q,u=k.i+=a,v=b;k.iw&&(w=s),k.i=w,f.text=v.substr(f.i,k.i-f.i),k.commentStore.push(f);continue}if("*"===e){var x=v.substr(k.i),y=x.match(/^\/\*(?:[^*]|\*+[^\/*])*\*+\//);if(y){f={index:k.i,text:y[0],isLineComment:!1},k.i+=f.text.length-1,k.commentStore.push(f);continue}}break}if(c!==l&&c!==n&&c!==m&&c!==o)break}if(h=h.slice(a+k.i-u+q),i=k.i,!h.length){if(ds||p>a||a===r||a===q},k.start=function(a,f,j){b=a,k.i=d=i=e=0,g=f?c(a,j):[a],h=g[0],t(0)},k.end=function(){var a,c=k.i>=b.length-1;return k.i=b.length-1,furthestChar:b[k.i]}},k}},{"./chunker":31}],33:[function(a,b){var c=a("../less-error"),d=a("../tree"),e=a("../visitors"),f=a("./parser-input"),g=a("../utils"),h=function i(a,b,h){function j(a,b){var c="[object Function]"===Object.prototype.toString.call(a)?a.call(n):o.$(a);return c?c:void l(b||("string"==typeof a?"expected '"+a+"' got '"+o.currentChar()+"'":"unexpected token"))}function k(a,b){return o.$char(a)?a:void l(b||"expected '"+a+"' got '"+o.currentChar()+"'")}function l(a,d){throw new c({index:o.i,filename:h.filename,type:d||"Syntax",message:a},b)}function m(a){var b=h.filename;return{lineNumber:g.getLocation(a,o.getInput()).line+1,fileName:b}}var n,o=f();return{parse:function(f,g,j){var k,l,m,n=null,p="";l=j&&j.globalVars?i.serializeVars(j.globalVars)+"\n":"",m=j&&j.modifyVars?"\n"+i.serializeVars(j.modifyVars):"",(l||j&&j.banner)&&(p=(j&&j.banner?j.banner:"")+l,b.contentsIgnoredChars[h.filename]=p.length),f=f.replace(/\r\n/g,"\n"),f=p+f.replace(/^\uFEFF/,"")+m,b.contents[h.filename]=f;try{o.start(f,a.chunkInput,function(a,d){throw c({index:d,type:"Parse",message:a,filename:h.filename},b)}),k=new d.Ruleset(null,this.parsers.primary()),k.root=!0,k.firstRoot=!0}catch(q){return g(new c(q,b,h.filename))}var r=o.end();if(!r.isFinished){var s=r.furthestPossibleErrorMessage;s||(s="Unrecognised input","}"===r.furthestChar?s+=". Possibly missing opening '{'":")"===r.furthestChar?s+=". Possibly missing opening '('":r.furthestReachedEnd&&(s+=". Possibly missing something")),n=new c({type:"Parse",message:s,index:r.furthest,filename:h.filename},b)}var t=function(a){return a=n||a||b.error,a?(a instanceof c||(a=new c(a,b,h.filename)),g(a)):g(null,k)};return a.processImports===!1?t():void new e.ImportVisitor(b,t).run(k)},parsers:n={primary:function(){for(var a,b=this.mixin,c=[];!o.finished;){for(;;){if(a=this.comment(),!a)break;c.push(a)}if(o.peek("}"))break;if(a=this.extendRule()||b.definition()||this.rule()||this.ruleset()||b.call()||this.rulesetCall()||this.directive())c.push(a);else if(!o.$re(/^[\s\n]+/)&&!o.$re(/^;+/))break}return c},comment:function(){if(o.commentStore.length){var a=o.commentStore.shift();return new d.Comment(a.text,a.isLineComment,a.index,h)}},entities:{quoted:function(){var a,b=o.i;return a=o.$re(/^(~)?("((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)')/),a?new d.Quoted(a[2],a[3]||a[4],Boolean(a[1]),b,h):void 0},keyword:function(){var a=o.$re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);return a?d.Color.fromKeyword(a)||new d.Keyword(a):void 0},call:function(){var a,b,c,e,f=o.i;if(!o.peek(/^url\(/i))return o.save(),(a=o.$re(/^([\w-]+|%|progid:[\w\.]+)\(/))?(a=a[1],b=a.toLowerCase(),"alpha"===b&&(e=n.alpha())?e:(c=this.arguments(),o.$char(")")?(o.forget(),new d.Call(a,c,f,h)):void o.restore("Could not parse call arguments or missing ')'"))):void o.forget()},arguments:function(){for(var a,b=[];;){if(a=this.assignment()||n.expression(),!a)break;if(b.push(a),!o.$char(","))break}return b},literal:function(){return this.dimension()||this.color()||this.quoted()||this.unicodeDescriptor()},assignment:function(){var a,b;return a=o.$re(/^\w+(?=\s?=)/i),a&&o.$char("=")?(b=n.entity(),b?new d.Assignment(a,b):void 0):void 0},url:function(){var a,b=o.i;if("u"===o.currentChar()&&o.$re(/^url\(/))return o.autoCommentAbsorb=!1,a=this.quoted()||this.variable()||o.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/)||"",o.autoCommentAbsorb=!0,k(")"),new d.URL(null!=a.value||a instanceof d.Variable?a:new d.Anonymous(a),b,h)},variable:function(){var a,b=o.i;return"@"===o.currentChar()&&(a=o.$re(/^@@?[\w-]+/))?new d.Variable(a,b,h):void 0},variableCurly:function(){var a,b=o.i;return"@"===o.currentChar()&&(a=o.$re(/^@\{([\w-]+)\}/))?new d.Variable("@"+a[1],b,h):void 0},color:function(){var a;if("#"===o.currentChar()&&(a=o.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))){var b=a.input.match(/^#([\w]+).*/);return b=b[1],b.match(/^[A-Fa-f0-9]+$/)||l("Invalid HEX color code"),new d.Color(a[1])}},dimension:function(){if(!o.peekNotNumeric()){var a=o.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i);return a?new d.Dimension(a[1],a[2]):void 0}},unicodeDescriptor:function(){var a;return a=o.$re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/),a?new d.UnicodeDescriptor(a[0]):void 0},javascript:function(){var a,b=o.i;return a=o.$re(/^(~)?`([^`]*)`/),a?new d.JavaScript(a[2],Boolean(a[1]),b,h):void 0}},variable:function(){var a;return"@"===o.currentChar()&&(a=o.$re(/^(@[\w-]+)\s*:/))?a[1]:void 0},rulesetCall:function(){var a;return"@"===o.currentChar()&&(a=o.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))?new d.RulesetCall(a[1]):void 0},extend:function(a){var b,c,e,f,g,h=o.i;if(o.$re(a?/^&:extend\(/:/^:extend\(/)){do{for(e=null,b=null;!(e=o.$re(/^(all)(?=\s*(\)|,))/))&&(c=this.element());)b?b.push(c):b=[c];e=e&&e[1],b||l("Missing target selector for :extend()."),g=new d.Extend(new d.Selector(b),e,h),f?f.push(g):f=[g]}while(o.$char(","));return j(/^\)/),a&&j(/^;/),f}},extendRule:function(){return this.extend(!0)},mixin:{call:function(){var a,b,c,e,f,g,i=o.currentChar(),j=!1,l=o.i;if("."===i||"#"===i){for(o.save();;){if(a=o.i,e=o.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/),!e)break;c=new d.Element(f,e,a,h),b?b.push(c):b=[c],f=o.$char(">")}return b&&(o.$char("(")&&(g=this.args(!0).args,k(")")),n.important()&&(j=!0),n.end())?(o.forget(),new d.mixin.Call(b,g,l,h,j)):void o.restore()}},args:function(a){var b,c,e,f,g,h,i=n.entities,j={args:null,variadic:!1},k=[],m=[],p=[];for(o.save();;){if(a)h=n.detachedRuleset()||n.expression();else{if(o.commentStore.length=0,"."===o.currentChar()&&o.$re(/^\.{3}/)){j.variadic=!0,o.$char(";")&&!b&&(b=!0),(b?m:p).push({variadic:!0});break}h=i.variable()||i.literal()||i.keyword()}if(!h)break;f=null,h.throwAwayComments&&h.throwAwayComments(),g=h;var q=null;if(a?h.value&&1==h.value.length&&(q=h.value[0]):q=h,q&&q instanceof d.Variable)if(o.$char(":")){if(k.length>0&&(b&&l("Cannot mix ; and , as delimiter types"),c=!0),g=a&&n.detachedRuleset()||n.expression(),!g){if(!a)return o.restore(),j.args=[],j;l("could not understand value for named argument")}f=e=q.name}else{if(!a&&o.$re(/^\.{3}/)){j.variadic=!0,o.$char(";")&&!b&&(b=!0),(b?m:p).push({name:h.name,variadic:!0});break}a||(e=f=q.name,g=null)}g&&k.push(g),p.push({name:f,value:g}),o.$char(",")||(o.$char(";")||b)&&(c&&l("Cannot mix ; and , as delimiter types"),b=!0,k.length>1&&(g=new d.Value(k)),m.push({name:e,value:g}),e=null,k=[],c=!1)}return o.forget(),j.args=b?m:p,j},definition:function(){var a,b,c,e,f=[],g=!1;if(!("."!==o.currentChar()&&"#"!==o.currentChar()||o.peek(/^[^{]*\}/)))if(o.save(),b=o.$re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)){a=b[1];var h=this.args(!1);if(f=h.args,g=h.variadic,!o.$char(")"))return void o.restore("Missing closing ')'");if(o.commentStore.length=0,o.$re(/^when/)&&(e=j(n.conditions,"expected condition")),c=n.block())return o.forget(),new d.mixin.Definition(a,f,c,e,g);o.restore()}else o.forget()}},entity:function(){var a=this.entities;return this.comment()||a.literal()||a.variable()||a.url()||a.call()||a.keyword()||a.javascript()},end:function(){return o.$char(";")||o.peek("}")},alpha:function(){var a;if(o.$re(/^opacity=/i))return a=o.$re(/^\d+/),a||(a=j(this.entities.variable,"Could not parse alpha")),k(")"),new d.Alpha(a)},element:function(){var a,b,c,e=o.i;return b=this.combinator(),a=o.$re(/^(?:\d+\.\d+|\d+)%/)||o.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)||o.$char("*")||o.$char("&")||this.attribute()||o.$re(/^\([^()@]+\)/)||o.$re(/^[\.#](?=@)/)||this.entities.variableCurly(),a||(o.save(),o.$char("(")?(c=this.selector())&&o.$char(")")?(a=new d.Paren(c),o.forget()):o.restore("Missing closing ')'"):o.forget()),a?new d.Element(b,a,e,h):void 0},combinator:function(){var a=o.currentChar();if("/"===a){o.save();var b=o.$re(/^\/[a-z]+\//i);if(b)return o.forget(),new d.Combinator(b);o.restore()}if(">"===a||"+"===a||"~"===a||"|"===a||"^"===a){for(o.i++,"^"===a&&"^"===o.currentChar()&&(a="^^",o.i++);o.isWhitespace();)o.i++;return new d.Combinator(a)}return new d.Combinator(o.isWhitespace(-1)?" ":null)},lessSelector:function(){return this.selector(!0)},selector:function(a){for(var b,c,e,f,g,i,k,m=o.i;(a&&(g=this.extend())||a&&(i=o.$re(/^when/))||(f=this.element()))&&(i?k=j(this.conditions,"expected condition"):k?l("CSS guard can only be used at the end of selector"):g?c?c.push(g):c=[g]:(c&&l("Extend can only be used at the end of selector"),e=o.currentChar(),b?b.push(f):b=[f],f=null),"{"!==e&&"}"!==e&&";"!==e&&","!==e&&")"!==e););return b?new d.Selector(b,c,k,m,h):void(c&&l("Extend must be used to extend a selector, it cannot be used on its own"))},attribute:function(){if(o.$char("[")){var a,b,c,e=this.entities;return(a=e.variableCurly())||(a=j(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/)),c=o.$re(/^[|~*$^]?=/),c&&(b=e.quoted()||o.$re(/^[0-9]+%/)||o.$re(/^[\w-]+/)||e.variableCurly()),k("]"),new d.Attribute(a,c,b)}},block:function(){var a;return o.$char("{")&&(a=this.primary())&&o.$char("}")?a:void 0},blockRuleset:function(){var a=this.block();return a&&(a=new d.Ruleset(null,a)),a},detachedRuleset:function(){var a=this.blockRuleset();return a?new d.DetachedRuleset(a):void 0},ruleset:function(){var b,c,e,f;for(o.save(),a.dumpLineNumbers&&(f=m(o.i));;){if(c=this.lessSelector(),!c)break;if(b?b.push(c):b=[c],o.commentStore.length=0,c.condition&&b.length>1&&l("Guards are only currently allowed on a single selector."),!o.$char(","))break;c.condition&&l("Guards are only currently allowed on a single selector."),o.commentStore.length=0}if(b&&(e=this.block())){o.forget();var g=new d.Ruleset(b,e,a.strictImports);return a.dumpLineNumbers&&(g.debugInfo=f),g}o.restore()},rule:function(b){var c,e,f,g,i,j=o.i,k=o.currentChar();if("."!==k&&"#"!==k&&"&"!==k)if(o.save(),c=this.variable()||this.ruleProperty()){if(i="string"==typeof c,i&&(e=this.detachedRuleset()),o.commentStore.length=0,!e){g=!i&&c.pop().value;var l=!b&&(a.compress||i);if(l&&(e=this.value()),!e&&(e=this.anonymousValue()))return o.forget(),new d.Rule(c,e,!1,g,j,h);l||e||(e=this.value()),f=this.important()}if(e&&this.end())return o.forget(),new d.Rule(c,e,f,g,j,h);if(o.restore(),e&&!b)return this.rule(!0)}else o.forget()},anonymousValue:function(){var a=o.$re(/^([^@+\/'"*`(;{}-]*);/);return a?new d.Anonymous(a[1]):void 0},"import":function(){var a,b,c=o.i,e=o.$re(/^@import?\s+/);if(e){var f=(e?this.importOptions():null)||{};if(a=this.entities.quoted()||this.entities.url())return b=this.mediaFeatures(),o.$(";")||(o.i=c,l("missing semi-colon or unrecognised media features on import")),b=b&&new d.Value(b),new d.Import(a,b,f,c,h);o.i=c,l("malformed import statement")}},importOptions:function(){var a,b,c,d={};if(!o.$char("("))return null;do if(a=this.importOption()){switch(b=a,c=!0,b){case"css":b="less",c=!1;break;case"once":b="multiple",c=!1}if(d[b]=c,!o.$char(","))break}while(a);return k(")"),d},importOption:function(){var a=o.$re(/^(less|css|multiple|once|inline|reference)/);return a?a[1]:void 0},mediaFeature:function(){var a,b,c=this.entities,e=[];o.save();do if(a=c.keyword()||c.variable())e.push(a);else if(o.$char("(")){if(b=this.property(),a=this.value(),!o.$char(")"))return o.restore("Missing closing ')'"),null;if(b&&a)e.push(new d.Paren(new d.Rule(b,a,null,null,o.i,h,!0)));else{if(!a)return o.restore("badly formed media feature definition"),null;e.push(new d.Paren(a))}}while(a);return o.forget(),e.length>0?new d.Expression(e):void 0},mediaFeatures:function(){var a,b=this.entities,c=[];do if(a=this.mediaFeature()){if(c.push(a),!o.$char(","))break}else if(a=b.variable(),a&&(c.push(a),!o.$char(",")))break;while(a);return c.length>0?c:null},media:function(){var b,c,e,f;return a.dumpLineNumbers&&(f=m(o.i)),o.$re(/^@media/)&&(b=this.mediaFeatures(),c=this.block())?(e=new d.Media(c,b,o.i,h),a.dumpLineNumbers&&(e.debugInfo=f),e):void 0},directive:function(){var b,c,e,f,g,i,j,k=o.i,n=!0;if("@"===o.currentChar()){if(c=this["import"]()||this.media())return c;if(o.save(),b=o.$re(/^@[a-z-]+/)){switch(f=b,"-"==b.charAt(1)&&b.indexOf("-",2)>0&&(f="@"+b.slice(b.indexOf("-",2)+1)),f){case"@charset":g=!0,n=!1;break;case"@namespace":i=!0,n=!1;break;case"@keyframes":g=!0;break;case"@host":case"@page":case"@document":case"@supports":j=!0}return o.commentStore.length=0,g?(c=this.entity(),c||l("expected "+b+" identifier")):i?(c=this.expression(),c||l("expected "+b+" expression")):j&&(c=(o.$re(/^[^{;]+/)||"").trim(),c&&(c=new d.Anonymous(c))),n&&(e=this.blockRuleset()),e||!n&&c&&o.$char(";")?(o.forget(),new d.Directive(b,c,e,k,h,a.dumpLineNumbers?m(k):null)):void o.restore("directive options not recognised")}}},value:function(){var a,b=[];do if(a=this.expression(),a&&(b.push(a),!o.$char(",")))break;while(a);return b.length>0?new d.Value(b):void 0},important:function(){return"!"===o.currentChar()?o.$re(/^! *important/):void 0},sub:function(){var a,b;return o.$char("(")&&(a=this.addition())?(b=new d.Expression([a]),k(")"),b.parens=!0,b):void 0},multiplication:function(){var a,b,c,e,f;if(a=this.operand()){for(f=o.isWhitespace(-1);;){if(o.peek(/^\/[*\/]/))break;if(o.save(),c=o.$char("/")||o.$char("*"),!c){o.forget();break}if(b=this.operand(),!b){o.restore();break}o.forget(),a.parensInOp=!0,b.parensInOp=!0,e=new d.Operation(c,[e||a,b],f),f=o.isWhitespace(-1)}return e||a}},addition:function(){var a,b,c,e,f;if(a=this.multiplication()){for(f=o.isWhitespace(-1);;){if(c=o.$re(/^[-+]\s+/)||!f&&(o.$char("+")||o.$char("-")),!c)break;if(b=this.multiplication(),!b)break;a.parensInOp=!0,b.parensInOp=!0,e=new d.Operation(c,[e||a,b],f),f=o.isWhitespace(-1)}return e||a}},conditions:function(){var a,b,c,e=o.i;if(a=this.condition()){for(;;){if(!o.peek(/^,\s*(not\s*)?\(/)||!o.$char(","))break;if(b=this.condition(),!b)break;c=new d.Condition("or",c||a,b,e)}return c||a}},condition:function(){var a,b,c,e,f=this.entities,g=o.i,h=!1;return o.$re(/^not/)&&(h=!0),k("("),a=this.addition()||f.keyword()||f.quoted(),a?(e=o.$re(/^(?:>=|<=|=<|[<=>])/),e?(b=this.addition()||f.keyword()||f.quoted(),b?c=new d.Condition(e,a,b,g,h):l("expected expression")):c=new d.Condition("=",a,new d.Keyword("true"),g,h),k(")"),o.$re(/^and/)?new d.Condition("and",c,this.condition()):c):void 0},operand:function(){var a,b=this.entities;o.peek(/^-[@\(]/)&&(a=o.$char("-"));var c=this.sub()||b.dimension()||b.color()||b.variable()||b.call();return a&&(c.parensInOp=!0,c=new d.Negative(c)),c},expression:function(){var a,b,c=[];do a=this.comment(),a?c.push(a):(a=this.addition()||this.entity(),a&&(c.push(a),o.peek(/^\/[\/*]/)||(b=o.$char("/"),b&&c.push(new d.Anonymous(b)))));while(a);return c.length>0?new d.Expression(c):void 0},property:function(){var a=o.$re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);return a?a[1]:void 0},ruleProperty:function(){function a(a){var b=o.i,c=o.$re(a);return c?(f.push(b),e.push(c[1])):void 0}var b,c,e=[],f=[];for(o.save(),a(/^(\*?)/);;)if(!a(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/))break;if(e.length>1&&a(/^((?:\+_|\+)?)\s*:/)){for(o.forget(),""===e[0]&&(e.shift(),f.shift()),c=0;c=b);c++);this.postProcessors.splice(c,0,{postProcessor:a,priority:b})},c.prototype.addFileManager=function(a){this.fileManagers.push(a)},c.prototype.getPostProcessors=function(){for(var a=[],b=0;bc&&(c=0),j=j.slice(this._contentsIgnoredCharsMap[b.filename])),j=j.substring(0,c),f=j.split("\n"),h=f[f.length-1]}if(e=a.split("\n"),g=e[e.length-1],b)if(d)for(i=0;i0){var e,f=JSON.stringify(this._sourceMapGenerator.toJSON());this.sourceMapURL?e=this.sourceMapURL:this._sourceMapFilename&&(e=this._sourceMapFilename),this.sourceMapURL=e,this._sourceMapFileInline?e="data:application/json;base64,"+a.encodeBase64(f):this.sourceMap=f,e&&this._css.push("/*# sourceMappingURL="+e+" */")}return this._css.join("")},b}},{}],38:[function(a,b){var c=a("./contexts"),d=a("./visitors"),e=a("./tree");b.exports=function(a,b){b=b||{};var f,g=b.variables,h=new c.Eval(b);"object"!=typeof g||Array.isArray(g)||(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof e.Value||(b instanceof e.Expression||(b=new e.Expression([b])),b=new e.Value([b])),new e.Rule("@"+a,b,!1,null,0)}),h.frames=[new e.Ruleset(null,g)]);var i,j=[],k=[new d.JoinSelectorVisitor,new d.ExtendVisitor,new d.ToCSSVisitor({compress:Boolean(b.compress)})];if(b.pluginManager){var l=b.pluginManager.getVisitors();for(i=0;ia?"0":"")+a.toString(16)}).join("")}var e=a("./node"),f=a("../data/colors"),g=function(a,b){this.rgb=Array.isArray(a)?a:6==a.length?a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha="number"==typeof b?b:1};g.prototype=new e,g.prototype.type="Color",g.prototype.luma=function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255; +return a=.03928>=a?a/12.92:Math.pow((a+.055)/1.055,2.4),b=.03928>=b?b/12.92:Math.pow((b+.055)/1.055,2.4),c=.03928>=c?c/12.92:Math.pow((c+.055)/1.055,2.4),.2126*a+.7152*b+.0722*c},g.prototype.genCSS=function(a,b){b.add(this.toCSS(a))},g.prototype.toCSS=function(a,b){var d,e,f=a&&a.compress&&!b;if(this.keyword)return this.keyword;if(e=this.fround(a,this.alpha),1>e)return"rgba("+this.rgb.map(function(a){return c(Math.round(a),255)}).concat(c(e,1)).join(","+(f?"":" "))+")";if(d=this.toRGB(),f){var g=d.split("");g[1]===g[2]&&g[3]===g[4]&&g[5]===g[6]&&(d="#"+g[1]+g[3]+g[5])}return d},g.prototype.operate=function(a,b,c){for(var d=[],e=this.alpha*(1-c.alpha)+c.alpha,f=0;3>f;f++)d[f]=this._operate(a,b,this.rgb[f],c.rgb[f]);return new g(d,e)},g.prototype.toRGB=function(){return d(this.rgb)},g.prototype.toHSL=function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=(g+h)/2,j=g-h;if(g===h)a=b=0;else{switch(b=i>.5?j/(2-g-h):j/(g+h),g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,l:i,a:f}},g.prototype.toHSV=function(){var a,b,c=this.rgb[0]/255,d=this.rgb[1]/255,e=this.rgb[2]/255,f=this.alpha,g=Math.max(c,d,e),h=Math.min(c,d,e),i=g,j=g-h;if(b=0===g?0:j/g,g===h)a=0;else{switch(g){case c:a=(d-e)/j+(e>d?6:0);break;case d:a=(e-c)/j+2;break;case e:a=(c-d)/j+4}a/=6}return{h:360*a,s:b,v:i,a:f}},g.prototype.toARGB=function(){return d([255*this.alpha].concat(this.rgb))},g.prototype.compare=function(a){return a.rgb&&a.rgb[0]===this.rgb[0]&&a.rgb[1]===this.rgb[1]&&a.rgb[2]===this.rgb[2]&&a.alpha===this.alpha?0:void 0},g.fromKeyword=function(a){var b,c=a.toLowerCase();return f.hasOwnProperty(c)?b=new g(f[c].slice(1)):"transparent"===c&&(b=new g([0,0,0],0)),b?(b.keyword=a,b):void 0},b.exports=g},{"../data/colors":9,"./node":64}],45:[function(a,b){var c=a("./node"),d=function(a){" "===a?(this.value=" ",this.emptyOrWhitespace=!0):(this.value=a?a.trim():"",this.emptyOrWhitespace=""===this.value)};d.prototype=new c,d.prototype.type="Combinator";var e={"":!0," ":!0,"|":!0};d.prototype.genCSS=function(a,b){var c=a.compress||e[this.value]?"":" ";b.add(c+this.value+c)},b.exports=d},{"./node":64}],46:[function(a,b){var c=a("./node"),d=a("./debug-info"),e=function(a,b,c,d){this.value=a,this.isLineComment=b,this.currentFileInfo=d};e.prototype=new c,e.prototype.type="Comment",e.prototype.genCSS=function(a,b){this.debugInfo&&b.add(d(a,this),this.currentFileInfo,this.index),b.add(this.value)},e.prototype.isSilent=function(a){var b=this.currentFileInfo&&this.currentFileInfo.reference&&!this.isReferenced,c=a.compress&&"!"!==this.value[2];return this.isLineComment||b||c},e.prototype.markReferenced=function(){this.isReferenced=!0},e.prototype.isRulesetLike=function(a){return Boolean(a)},b.exports=e},{"./debug-info":48,"./node":64}],47:[function(a,b){var c=a("./node"),d=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e};d.prototype=new c,d.prototype.type="Condition",d.prototype.accept=function(a){this.lvalue=a.visit(this.lvalue),this.rvalue=a.visit(this.rvalue)},d.prototype.eval=function(a){var b=function(a,b,d){switch(a){case"and":return b&&d;case"or":return b||d;default:switch(c.compare(b,d)){case-1:return"<"===a||"=<"===a||"<="===a;case 0:return"="===a||">="===a||"=<"===a||"<="===a;case 1:return">"===a||">="===a;default:return!1}}}(this.op,this.lvalue.eval(a),this.rvalue.eval(a));return this.negate?!b:b},b.exports=d},{"./node":64}],48:[function(a,b){var c=function(a,b,d){var e="";if(a.dumpLineNumbers&&!a.compress)switch(a.dumpLineNumbers){case"comments":e=c.asComment(b);break;case"mediaquery":e=c.asMediaQuery(b);break;case"all":e=c.asComment(b)+(d||"")+c.asMediaQuery(b)}return e};c.asComment=function(a){return"/* line "+a.debugInfo.lineNumber+", "+a.debugInfo.fileName+" */\n"},c.asMediaQuery=function(a){return"@media -sass-debug-info{filename{font-family:"+("file://"+a.debugInfo.fileName).replace(/([.:\/\\])/g,function(a){return"\\"==a&&(a="/"),"\\"+a})+"}line{font-family:\\00003"+a.debugInfo.lineNumber+"}}\n"},b.exports=c},{}],49:[function(a,b){var c=a("./node"),d=a("../contexts"),e=function(a,b){this.ruleset=a,this.frames=b};e.prototype=new c,e.prototype.type="DetachedRuleset",e.prototype.evalFirst=!0,e.prototype.accept=function(a){this.ruleset=a.visit(this.ruleset)},e.prototype.eval=function(a){var b=this.frames||a.frames.slice(0);return new e(this.ruleset,b)},e.prototype.callEval=function(a){return this.ruleset.eval(this.frames?new d.Eval(a,this.frames.concat(a.frames)):a)},b.exports=e},{"../contexts":8,"./node":64}],50:[function(a,b){var c=a("./node"),d=a("../data/unit-conversions"),e=a("./unit"),f=a("./color"),g=function(a,b){this.value=parseFloat(a),this.unit=b&&b instanceof e?b:new e(b?[b]:void 0)};g.prototype=new c,g.prototype.type="Dimension",g.prototype.accept=function(a){this.unit=a.visit(this.unit)},g.prototype.eval=function(){return this},g.prototype.toColor=function(){return new f([this.value,this.value,this.value])},g.prototype.genCSS=function(a,b){if(a&&a.strictUnits&&!this.unit.isSingular())throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());var c=this.fround(a,this.value),d=String(c);if(0!==c&&1e-6>c&&c>-1e-6&&(d=c.toFixed(20).replace(/0+$/,"")),a&&a.compress){if(0===c&&this.unit.isLength())return void b.add(d);c>0&&1>c&&(d=d.substr(1))}b.add(d),this.unit.genCSS(a,b)},g.prototype.operate=function(a,b,c){var d=this._operate(a,b,this.value,c.value),e=this.unit.clone();if("+"===b||"-"===b)if(0===e.numerator.length&&0===e.denominator.length)e.numerator=c.unit.numerator.slice(0),e.denominator=c.unit.denominator.slice(0);else if(0===c.unit.numerator.length&&0===e.denominator.length);else{if(c=c.convertTo(this.unit.usedUnits()),a.strictUnits&&c.unit.toString()!==e.toString())throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '"+e.toString()+"' and '"+c.unit.toString()+"'.");d=this._operate(a,b,this.value,c.value)}else"*"===b?(e.numerator=e.numerator.concat(c.unit.numerator).sort(),e.denominator=e.denominator.concat(c.unit.denominator).sort(),e.cancel()):"/"===b&&(e.numerator=e.numerator.concat(c.unit.denominator).sort(),e.denominator=e.denominator.concat(c.unit.numerator).sort(),e.cancel());return new g(d,e)},g.prototype.compare=function(a){var b,d;if(!(a instanceof g))return void 0;if(this.unit.isEmpty()||a.unit.isEmpty())b=this,d=a;else if(b=this.unify(),d=a.unify(),0!==b.unit.compare(d.unit))return void 0;return c.numericCompare(b.value,d.value)},g.prototype.unify=function(){return this.convertTo({length:"px",duration:"s",angle:"rad"})},g.prototype.convertTo=function(a){var b,c,e,f,h,i=this.value,j=this.unit.clone(),k={};if("string"==typeof a){for(b in d)d[b].hasOwnProperty(a)&&(k={},k[b]=a);a=k}h=function(a,b){return e.hasOwnProperty(a)?(b?i/=e[a]/e[f]:i*=e[a]/e[f],f):a};for(c in a)a.hasOwnProperty(c)&&(f=a[c],e=d[c],j.map(h));return j.cancel(),new g(i,j)},b.exports=g},{"../data/unit-conversions":11,"./color":44,"./node":64,"./unit":73}],51:[function(a,b){var c=a("./node"),d=a("./ruleset"),e=function(a,b,c,d,e,f){this.name=a,this.value=b,c&&(this.rules=c,this.rules.allowImports=!0),this.index=d,this.currentFileInfo=e,this.debugInfo=f};e.prototype=new c,e.prototype.type="Directive",e.prototype.accept=function(a){var b=this.value,c=this.rules;c&&(this.rules=a.visit(c)),b&&(this.value=a.visit(b))},e.prototype.isRulesetLike=function(){return this.rules||!this.isCharset()},e.prototype.isCharset=function(){return"@charset"===this.name},e.prototype.genCSS=function(a,b){var c=this.value,d=this.rules;b.add(this.name,this.currentFileInfo,this.index),c&&(b.add(" "),c.genCSS(a,b)),d?this.outputRuleset(a,b,[d]):b.add(";")},e.prototype.eval=function(a){var b=this.value,c=this.rules;return b&&(b=b.eval(a)),c&&(c=c.eval(a),c.root=!0),new e(this.name,b,c,this.index,this.currentFileInfo,this.debugInfo)},e.prototype.variable=function(a){return this.rules?d.prototype.variable.call(this.rules,a):void 0},e.prototype.find=function(){return this.rules?d.prototype.find.apply(this.rules,arguments):void 0},e.prototype.rulesets=function(){return this.rules?d.prototype.rulesets.apply(this.rules):void 0},e.prototype.markReferenced=function(){var a,b;if(this.isReferenced=!0,this.rules)for(b=this.rules.rules,a=0;ad;d++)c[d].genCSS(a,b);return b.add("}"),void a.tabLevel--}var f="\n"+Array(a.tabLevel).join(" "),g=f+" ";if(e){for(b.add(" {"+g),c[0].genCSS(a,b),d=1;e>d;d++)b.add(g),c[d].genCSS(a,b);b.add(f+"}")}else b.add(" {"+f+"}");a.tabLevel--},b.exports=e},{"./node":64,"./ruleset":70}],52:[function(a,b){var c=a("./node"),d=a("./paren"),e=a("./combinator"),f=function(a,b,c,d){this.combinator=a instanceof e?a:new e(a),this.value="string"==typeof b?b.trim():b?b:"",this.index=c,this.currentFileInfo=d};f.prototype=new c,f.prototype.type="Element",f.prototype.accept=function(a){var b=this.value;this.combinator=a.visit(this.combinator),"object"==typeof b&&(this.value=a.visit(b))},f.prototype.eval=function(a){return new f(this.combinator,this.value.eval?this.value.eval(a):this.value,this.index,this.currentFileInfo)},f.prototype.genCSS=function(a,b){b.add(this.toCSS(a),this.currentFileInfo,this.index)},f.prototype.toCSS=function(a){a=a||{};var b=this.value,c=a.firstSelector;return b instanceof d&&(a.firstSelector=!0),b=b.toCSS?b.toCSS(a):b,a.firstSelector=c,""===b&&"&"===this.combinator.value.charAt(0)?"":this.combinator.toCSS(a)+b},b.exports=f},{"./combinator":45,"./node":64,"./paren":66}],53:[function(a,b){var c=a("./node"),d=a("./paren"),e=a("./comment"),f=function(a){if(this.value=a,!a)throw new Error("Expression requires a array parameter")};f.prototype=new c,f.prototype.type="Expression",f.prototype.accept=function(a){this.value=a.visitArray(this.value)},f.prototype.eval=function(a){var b,c=this.parens&&!this.parensInOp,e=!1;return c&&a.inParenthesis(),this.value.length>1?b=new f(this.value.map(function(b){return b.eval(a)})):1===this.value.length?(this.value[0].parens&&!this.value[0].parensInOp&&(e=!0),b=this.value[0].eval(a)):b=this,c&&a.outOfParenthesis(),this.parens&&this.parensInOp&&!a.isMathOn()&&!e&&(b=new d(b)),b},f.prototype.genCSS=function(a,b){for(var c=0;c0&&c.length&&""===c[0].combinator.value&&(c[0].combinator.value=" "),d=d.concat(a[b].elements);this.selfSelectors=[{elements:d}]},b.exports=d},{"./node":64}],55:[function(a,b){var c=a("./node"),d=a("./media"),e=a("./url"),f=a("./quoted"),g=a("./ruleset"),h=a("./anonymous"),i=function(a,b,c,d,e){if(this.options=c,this.index=d,this.path=a,this.features=b,this.currentFileInfo=e,void 0!==this.options.less||this.options.inline)this.css=!this.options.less||this.options.inline;else{var f=this.getPath();f&&/css([\?;].*)?$/.test(f)&&(this.css=!0)}};i.prototype=new c,i.prototype.type="Import",i.prototype.accept=function(a){this.features&&(this.features=a.visit(this.features)),this.path=a.visit(this.path),!this.options.inline&&this.root&&(this.root=a.visit(this.root))},i.prototype.genCSS=function(a,b){this.css&&(b.add("@import ",this.currentFileInfo,this.index),this.path.genCSS(a,b),this.features&&(b.add(" "),this.features.genCSS(a,b)),b.add(";"))},i.prototype.getPath=function(){if(this.path instanceof f){var a=this.path.value;return void 0!==this.css||/(\.[a-z]*$)|([\?;].*)$/.test(a)?a:a+".less"}return this.path instanceof e?this.path.value.value:null},i.prototype.evalForImport=function(a){var b=this.path;return b instanceof e&&(b=b.value),new i(b.eval(a),this.features,this.options,this.index,this.currentFileInfo)},i.prototype.evalPath=function(a){var b=this.path.eval(a),c=this.currentFileInfo&&this.currentFileInfo.rootpath;if(!(b instanceof e)){if(c){var d=b.value;d&&a.isPathRelative(d)&&(b.value=c+d)}b.value=a.normalizePath(b.value)}return b},i.prototype.eval=function(a){var b,c=this.features&&this.features.eval(a);if(this.skip&&("function"==typeof this.skip&&(this.skip=this.skip()),this.skip))return[];if(this.options.inline){var e=new h(this.root,0,{filename:this.importedFilename},!0,!0);return this.features?new d([e],this.features.value):[e]}if(this.css){var f=new i(this.evalPath(a),c,this.options,this.index);if(!f.css&&this.error)throw this.error;return f}return b=new g(null,this.root.rules.slice(0)),b.evalImports(a),this.features?new d(b.rules,this.features.value):b.rules},b.exports=i},{"./anonymous":40,"./media":60,"./node":64,"./quoted":67,"./ruleset":70,"./url":74}],56:[function(a,b){var c={};c.Alpha=a("./alpha"),c.Color=a("./color"),c.Directive=a("./directive"),c.DetachedRuleset=a("./detached-ruleset"),c.Operation=a("./operation"),c.Dimension=a("./dimension"),c.Unit=a("./unit"),c.Keyword=a("./keyword"),c.Variable=a("./variable"),c.Ruleset=a("./ruleset"),c.Element=a("./element"),c.Attribute=a("./attribute"),c.Combinator=a("./combinator"),c.Selector=a("./selector"),c.Quoted=a("./quoted"),c.Expression=a("./expression"),c.Rule=a("./rule"),c.Call=a("./call"),c.URL=a("./url"),c.Import=a("./import"),c.mixin={Call:a("./mixin-call"),Definition:a("./mixin-definition")},c.Comment=a("./comment"),c.Anonymous=a("./anonymous"),c.Value=a("./value"),c.JavaScript=a("./javascript"),c.Assignment=a("./assignment"),c.Condition=a("./condition"),c.Paren=a("./paren"),c.Media=a("./media"),c.UnicodeDescriptor=a("./unicode-descriptor"),c.Negative=a("./negative"),c.Extend=a("./extend"),c.RulesetCall=a("./ruleset-call"),b.exports=c},{"./alpha":39,"./anonymous":40,"./assignment":41,"./attribute":42,"./call":43,"./color":44,"./combinator":45,"./comment":46,"./condition":47,"./detached-ruleset":49,"./dimension":50,"./directive":51,"./element":52,"./expression":53,"./extend":54,"./import":55,"./javascript":57,"./keyword":59,"./media":60,"./mixin-call":61,"./mixin-definition":62,"./negative":63,"./operation":65,"./paren":66,"./quoted":67,"./rule":68,"./ruleset":70,"./ruleset-call":69,"./selector":71,"./unicode-descriptor":72,"./unit":73,"./url":74,"./value":75,"./variable":76}],57:[function(a,b){var c=a("./js-eval-node"),d=a("./dimension"),e=a("./quoted"),f=a("./anonymous"),g=function(a,b,c,d){this.escaped=b,this.expression=a,this.index=c,this.currentFileInfo=d};g.prototype=new c,g.prototype.type="JavaScript",g.prototype.eval=function(a){var b=this.evaluateJavaScript(this.expression,a);return"number"==typeof b?new d(b):"string"==typeof b?new e('"'+b+'"',b,this.escaped,this.index):new f(Array.isArray(b)?b.join(", "):b)},b.exports=g},{"./anonymous":40,"./dimension":50,"./js-eval-node":58,"./quoted":67}],58:[function(a,b){var c=a("./node"),d=a("./variable"),e=function(){};e.prototype=new c,e.prototype.evaluateJavaScript=function(a,b){var c,e=this,f={};if(void 0!==b.javascriptEnabled&&!b.javascriptEnabled)throw{message:"You are using JavaScript, which has been disabled.",filename:this.currentFileInfo.filename,index:this.index};a=a.replace(/@\{([\w-]+)\}/g,function(a,c){return e.jsify(new d("@"+c,e.index,e.currentFileInfo).eval(b))});try{a=new Function("return ("+a+")")}catch(g){throw{message:"JavaScript evaluation error: "+g.message+" from `"+a+"`",filename:this.currentFileInfo.filename,index:this.index}}var h=b.frames[0].variables();for(var i in h)h.hasOwnProperty(i)&&(f[i.slice(1)]={value:h[i].value,toJS:function(){return this.value.eval(b).toCSS()}});try{c=a.call(f)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message.replace(/["]/g,"'")+"'",filename:this.currentFileInfo.filename,index:this.index}}return c},e.prototype.jsify=function(a){return Array.isArray(a.value)&&a.value.length>1?"["+a.value.map(function(a){return a.toCSS()}).join(", ")+"]":a.toCSS()},b.exports=e},{"./node":64,"./variable":76}],59:[function(a,b){var c=a("./node"),d=function(a){this.value=a};d.prototype=new c,d.prototype.type="Keyword",d.prototype.genCSS=function(a,b){if("%"===this.value)throw{type:"Syntax",message:"Invalid % without number"};b.add(this.value)},d.True=new d("true"),d.False=new d("false"),b.exports=d},{"./node":64}],60:[function(a,b){var c=a("./ruleset"),d=a("./value"),e=a("./element"),f=a("./selector"),g=a("./anonymous"),h=a("./expression"),i=a("./directive"),j=function(a,b,e,f){this.index=e,this.currentFileInfo=f;var g=this.emptySelectors();this.features=new d(b),this.rules=[new c(g,a)],this.rules[0].allowImports=!0};j.prototype=new i,j.prototype.type="Media",j.prototype.isRulesetLike=!0,j.prototype.accept=function(a){this.features&&(this.features=a.visit(this.features)),this.rules&&(this.rules=a.visitArray(this.rules))},j.prototype.genCSS=function(a,b){b.add("@media ",this.currentFileInfo,this.index),this.features.genCSS(a,b),this.outputRuleset(a,b,this.rules)},j.prototype.eval=function(a){a.mediaBlocks||(a.mediaBlocks=[],a.mediaPath=[]);var b=new j(null,[],this.index,this.currentFileInfo);this.debugInfo&&(this.rules[0].debugInfo=this.debugInfo,b.debugInfo=this.debugInfo);var c=!1;a.strictMath||(c=!0,a.strictMath=!0);try{b.features=this.features.eval(a)}finally{c&&(a.strictMath=!1)}return a.mediaPath.push(b),a.mediaBlocks.push(b),a.frames.unshift(this.rules[0]),b.rules=[this.rules[0].eval(a)],a.frames.shift(),a.mediaPath.pop(),0===a.mediaPath.length?b.evalTop(a):b.evalNested(a)},j.prototype.variable=function(a){return c.prototype.variable.call(this.rules[0],a)},j.prototype.find=function(){return c.prototype.find.apply(this.rules[0],arguments)},j.prototype.rulesets=function(){return c.prototype.rulesets.apply(this.rules[0])},j.prototype.emptySelectors=function(){var a=new e("","&",this.index,this.currentFileInfo),b=[new f([a],null,null,this.index,this.currentFileInfo)];return b[0].mediaEmpty=!0,b},j.prototype.markReferenced=function(){var a,b=this.rules[0].rules;for(this.rules[0].markReferenced(),this.isReferenced=!0,a=0;a1){var d=this.emptySelectors();b=new c(d,a.mediaBlocks),b.multiMedia=!0}return delete a.mediaBlocks,delete a.mediaPath,b},j.prototype.evalNested=function(a){var b,e,f=a.mediaPath.concat([this]);for(b=0;b0;b--)a.splice(b,0,new g("and"));return new h(a)})),new c([],[])},j.prototype.permute=function(a){if(0===a.length)return[];if(1===a.length)return a[0];for(var b=[],c=this.permute(a.slice(1)),d=0;dk;k++){for(w[k]=!0,f.value(k),d=0;d0){for(m=!0,j=0;j0)p=A;else if(p=z,q[z]+q[A]>1)throw{type:"Runtime",message:"Ambiguous use of `default()` found when matching for `"+this.format(h)+"`",index:this.index,filename:this.currentFileInfo.filename};for(j=0;jk;k++)if(j=c[k],n=j&&j.name){for(o=!1,l=0;ll;l++)i.push(c[l].value.eval(a));q.prependRule(new f(n,new g(i).eval(a)))}else{if(m=j&&j.value)m=m.eval(a);else{if(!r[k].value)throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+s+" for "+this.arity+")"};m=r[k].value.eval(b),q.resetCache()}q.prependRule(new f(n,m)),d[k]=m}if(r[k].variadic&&c)for(l=p;s>l;l++)d[l]=c[l].value.eval(a);p++}return q},i.prototype.eval=function(a){return new i(this.name,this.params,this.rules,this.condition,this.variadic,this.frames||a.frames.slice(0))},i.prototype.evalCall=function(a,b,c){var d,i,j=[],k=this.frames?this.frames.concat(a.frames):a.frames,l=this.evalParams(a,new h.Eval(a,k),b,j);return l.prependRule(new f("@arguments",new g(j).eval(a))),d=this.rules.slice(0),i=new e(null,d),i.originalRuleset=this,i=i.eval(new h.Eval(a,[this,l].concat(k))),c&&(i=this.makeImportant.apply(i)),i},i.prototype.matchCondition=function(a,b){return this.condition&&!this.condition.eval(new h.Eval(b,[this.evalParams(b,new h.Eval(b,this.frames?this.frames.concat(b.frames):b.frames),a,[])].concat(this.frames).concat(b.frames)))?!1:!0},i.prototype.matchArgs=function(a,b){var c,d=a&&a.length||0;if(this.variadic){if(dthis.params.length)return!1}c=Math.min(d,this.arity);for(var e=0;c>e;e++)if(!this.params[e].name&&!this.params[e].variadic&&a[e].value.eval(b).toCSS()!=this.params[e].value.eval(b).toCSS())return!1;return!0},b.exports=i},{"../contexts":8,"./element":52,"./expression":53,"./rule":68,"./ruleset":70,"./selector":71}],63:[function(a,b){var c=a("./node"),d=a("./operation"),e=a("./dimension"),f=function(a){this.value=a};f.prototype=new c,f.prototype.type="Negative",f.prototype.genCSS=function(a,b){b.add("-"),this.value.genCSS(a,b)},f.prototype.eval=function(a){return a.isMathOn()?new d("*",[new e(-1),this.value]).eval(a):new f(this.value.eval(a))},b.exports=f},{"./dimension":50,"./node":64,"./operation":65}],64:[function(a,b){var c=function(){};c.prototype.toCSS=function(a){var b=[];return this.genCSS(a,{add:function(a){b.push(a)},isEmpty:function(){return 0===b.length}}),b.join("")},c.prototype.genCSS=function(a,b){b.add(this.value)},c.prototype.accept=function(a){this.value=a.visit(this.value)},c.prototype.eval=function(){return this},c.prototype._operate=function(a,b,c,d){switch(b){case"+":return c+d;case"-":return c-d;case"*":return c*d;case"/":return c/d}},c.prototype.fround=function(a,b){var c=a&&a.numPrecision;return null==c?b:Number((b+2e-16).toFixed(c))},c.compare=function(a,b){if(a.compare&&"Quoted"!==b.type&&"Anonymous"!==b.type)return a.compare(b);if(b.compare)return-b.compare(a);if(a.type!==b.type)return void 0;if(a=a.value,b=b.value,!Array.isArray(a))return a===b?0:void 0;if(a.length!==b.length)return void 0;for(var d=0;da?-1:a===b?0:a>b?1:void 0},b.exports=c},{}],65:[function(a,b){var c=a("./node"),d=a("./color"),e=a("./dimension"),f=function(a,b,c){this.op=a.trim(),this.operands=b,this.isSpaced=c};f.prototype=new c,f.prototype.type="Operation",f.prototype.accept=function(a){this.operands=a.visit(this.operands)},f.prototype.eval=function(a){var b=this.operands[0].eval(a),c=this.operands[1].eval(a);if(a.isMathOn()){if(b instanceof e&&c instanceof d&&(b=b.toColor()),c instanceof e&&b instanceof d&&(c=c.toColor()),!b.operate)throw{type:"Operation",message:"Operation on an invalid type"};return b.operate(a,this.op,c)}return new f(this.op,[b,c],this.isSpaced)},f.prototype.genCSS=function(a,b){this.operands[0].genCSS(a,b),this.isSpaced&&b.add(" "),b.add(this.op),this.isSpaced&&b.add(" "),this.operands[1].genCSS(a,b)},b.exports=f},{"./color":44,"./dimension":50,"./node":64}],66:[function(a,b){var c=a("./node"),d=function(a){this.value=a};d.prototype=new c,d.prototype.type="Paren",d.prototype.genCSS=function(a,b){b.add("("),this.value.genCSS(a,b),b.add(")")},d.prototype.eval=function(a){return new d(this.value.eval(a))},b.exports=d},{"./node":64}],67:[function(a,b){var c=a("./node"),d=a("./js-eval-node"),e=a("./variable"),f=function(a,b,c,d,e){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d,this.currentFileInfo=e};f.prototype=new d,f.prototype.type="Quoted",f.prototype.genCSS=function(a,b){this.escaped||b.add(this.quote,this.currentFileInfo,this.index),b.add(this.value),this.escaped||b.add(this.quote)},f.prototype.eval=function(a){function b(a,b,c){var d=a;do a=d,d=a.replace(b,c);while(a!==d);return d}var c=this,d=this.value,g=function(b,d){return String(c.evaluateJavaScript(d,a))},h=function(b,d){var g=new e("@"+d,c.index,c.currentFileInfo).eval(a,!0);return g instanceof f?g.value:g.toCSS()};return d=b(d,/`([^`]+)`/g,g),d=b(d,/@\{([\w-]+)\}/g,h),new f(this.quote+d+this.quote,d,this.escaped,this.index,this.currentFileInfo)},f.prototype.compare=function(a){return"Quoted"!==a.type||this.escaped||a.escaped?a.toCSS&&this.toCSS()===a.toCSS()?0:void 0:c.numericCompare(this.value,a.value)},b.exports=f},{"./js-eval-node":58,"./node":64,"./variable":76}],68:[function(a,b){function c(a,b){var c,d="",e=b.length,f={add:function(a){d+=a}};for(c=0;e>c;c++)b[c].eval(a).genCSS(a,f);return d}var d=a("./node"),e=a("./value"),f=a("./keyword"),g=function(a,b,c,f,g,h,i,j){this.name=a,this.value=b instanceof d?b:new e([b]),this.important=c?" "+c.trim():"",this.merge=f,this.index=g,this.currentFileInfo=h,this.inline=i||!1,this.variable=void 0!==j?j:a.charAt&&"@"===a.charAt(0)};g.prototype=new d,g.prototype.type="Rule",g.prototype.genCSS=function(a,b){b.add(this.name+(a.compress?":":": "),this.currentFileInfo,this.index);try{this.value.genCSS(a,b)}catch(c){throw c.index=this.index,c.filename=this.currentFileInfo.filename,c}b.add(this.important+(this.inline||a.lastRule&&a.compress?"":";"),this.currentFileInfo,this.index)},g.prototype.eval=function(a){var b,d=!1,e=this.name,h=this.variable;"string"!=typeof e&&(e=1===e.length&&e[0]instanceof f?e[0].value:c(a,e),h=!1),"font"!==e||a.strictMath||(d=!0,a.strictMath=!0);try{if(b=this.value.eval(a),!this.variable&&"DetachedRuleset"===b.type)throw{message:"Rulesets cannot be evaluated on a property.",index:this.index,filename:this.currentFileInfo.filename};return new g(e,b,this.important,this.merge,this.index,this.currentFileInfo,this.inline,h)}catch(i){throw"number"!=typeof i.index&&(i.index=this.index,i.filename=this.currentFileInfo.filename),i}finally{d&&(a.strictMath=!1)}},g.prototype.makeImportant=function(){return new g(this.name,this.value,"!important",this.merge,this.index,this.currentFileInfo,this.inline)},b.exports=g},{"./keyword":59,"./node":64,"./value":75}],69:[function(a,b){var c=a("./node"),d=a("./variable"),e=function(a){this.variable=a};e.prototype=new c,e.prototype.type="RulesetCall",e.prototype.eval=function(a){var b=new d(this.variable).eval(a);return b.callEval(a)},b.exports=e},{"./node":64,"./variable":76}],70:[function(a,b){var c=a("./node"),d=a("./rule"),e=a("./selector"),f=a("./element"),g=a("../contexts"),h=a("../functions/default"),i=a("./debug-info"),j=function(a,b,c){this.selectors=a,this.rules=b,this._lookups={},this.strictImports=c};j.prototype=new c,j.prototype.type="Ruleset",j.prototype.isRuleset=!0,j.prototype.isRulesetLike=!0,j.prototype.accept=function(a){this.paths?a.visitArray(this.paths,!0):this.selectors&&(this.selectors=a.visitArray(this.selectors)),this.rules&&this.rules.length&&(this.rules=a.visitArray(this.rules))},j.prototype.eval=function(a){var b,c,e,f,g=this.selectors,i=!1;if(g&&(c=g.length)){for(b=[],h.error({type:"Syntax",message:"it is currently only allowed in parametric mixin guards,"}),f=0;c>f;f++)e=g[f].eval(a),b.push(e),e.evaldCondition&&(i=!0);h.reset()}else i=!0;var k,l,m=this.rules?this.rules.slice(0):null,n=new j(b,m,this.strictImports);n.originalRuleset=this,n.root=this.root,n.firstRoot=this.firstRoot,n.allowImports=this.allowImports,this.debugInfo&&(n.debugInfo=this.debugInfo),i||(m.length=0);var o=a.frames;o.unshift(n);var p=a.selectors;p||(a.selectors=p=[]),p.unshift(this.selectors),(n.root||n.allowImports||!n.strictImports)&&n.evalImports(a);var q=n.rules,r=q?q.length:0;for(f=0;r>f;f++)q[f].evalFirst&&(q[f]=q[f].eval(a));var s=a.mediaBlocks&&a.mediaBlocks.length||0;for(f=0;r>f;f++)"MixinCall"===q[f].type?(m=q[f].eval(a).filter(function(a){return a instanceof d&&a.variable?!n.variable(a.name):!0 +}),q.splice.apply(q,[f,1].concat(m)),r+=m.length-1,f+=m.length-1,n.resetCache()):"RulesetCall"===q[f].type&&(m=q[f].eval(a).rules.filter(function(a){return a instanceof d&&a.variable?!1:!0}),q.splice.apply(q,[f,1].concat(m)),r+=m.length-1,f+=m.length-1,n.resetCache());for(f=0;fa;a++)b=d[a],b.isRuleset&&c.push(b);return c},j.prototype.prependRule=function(a){var b=this.rules;b?b.unshift(a):this.rules=[a]},j.prototype.find=function(a,b,c){b=b||this;var d,f,g=[],h=a.toCSS();return h in this._lookups?this._lookups[h]:(this.rulesets().forEach(function(h){if(h!==b)for(var i=0;id){if(!c||c(h)){f=h.find(new e(a.elements.slice(d)),b,c);for(var j=0;jd;d++)if(j=r[d],q=j.length)for(d>0&&b.add(n),a.firstSelector=!0,j[0].genCSS(a,b),a.firstSelector=!1,e=1;q>e;e++)j[e].genCSS(a,b);b.add((a.compress?"{":" {\n")+o)}for(d=0;dd;d++)n&&b.add(n),m[d].genCSS(a,b);b.isEmpty()||a.compress||!this.firstRoot||b.add("\n")},j.prototype.markReferenced=function(){if(this.selectors)for(var a=0;a0&&this.mergeElementsOnToSelectors(r,i),e=0;e0&&(k[0].elements=k[0].elements.slice(0),k[0].elements.push(new f(j.combinator,"",j.index,j.currentFileInfo))),s.push(k);else for(g=0;g0?(m=k.slice(0),q=m.pop(),o=c.createDerived(q.elements.slice(0)),p=!1):o=c.createDerived([]),l.length>1&&(n=n.concat(l.slice(1))),l.length>0){p=!1;var t=j.combinator,u=l[0].elements[0];t.emptyOrWhitespace&&!u.combinator.emptyOrWhitespace&&(t=u.combinator),o.elements.push(new f(t,u.value,j.index,j.currentFileInfo)),o.elements=o.elements.concat(l[0].elements.slice(1))}p||m.push(o),m=m.concat(n),s.push(m)}i=s,r=[]}for(r.length>0&&this.mergeElementsOnToSelectors(r,i),d=0;d0&&a.push(i[d])}else if(b.length>0)for(d=0;d0?d[d.length-1]=d[d.length-1].createDerived(d[d.length-1].elements.concat(a)):d.push(new e(a))},b.exports=j},{"../contexts":8,"../functions/default":17,"./debug-info":48,"./element":52,"./node":64,"./rule":68,"./selector":71}],71:[function(a,b){var c=a("./node"),d=function(a,b,c,d,e,f){this.elements=a,this.extendList=b,this.condition=c,this.currentFileInfo=e||{},this.isReferenced=f,c||(this.evaldCondition=!0)};d.prototype=new c,d.prototype.type="Selector",d.prototype.accept=function(a){this.elements&&(this.elements=a.visitArray(this.elements)),this.extendList&&(this.extendList=a.visitArray(this.extendList)),this.condition&&(this.condition=a.visit(this.condition))},d.prototype.createDerived=function(a,b,c){c=null!=c?c:this.evaldCondition;var e=new d(a,b||this.extendList,null,this.index,this.currentFileInfo,this.isReferenced);return e.evaldCondition=c,e.mediaEmpty=this.mediaEmpty,e},d.prototype.match=function(a){var b,c,d=this.elements,e=d.length;if(a.CacheElements(),b=a._elements.length,0===b||b>e)return 0;for(c=0;b>c;c++)if(d[c].value!==a._elements[c])return 0;return b},d.prototype.CacheElements=function(){if(!this._elements){var a=this.elements.map(function(a){return a.combinator.value+(a.value.value||a.value)}).join("").match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);a?"&"===a[0]&&a.shift():a=[],this._elements=a}},d.prototype.isJustParentSelector=function(){return!this.mediaEmpty&&1===this.elements.length&&"&"===this.elements[0].value&&(" "===this.elements[0].combinator.value||""===this.elements[0].combinator.value)},d.prototype.eval=function(a){var b=this.condition&&this.condition.eval(a),c=this.elements,d=this.extendList;return c=c&&c.map(function(b){return b.eval(a)}),d=d&&d.map(function(b){return b.eval(a)}),this.createDerived(c,d,b)},d.prototype.genCSS=function(a,b){var c,d;if(a&&a.firstSelector||""!==this.elements[0].combinator.value||b.add(" ",this.currentFileInfo,this.index),!this._css)for(c=0;c=1?b.add(this.numerator[0]):this.denominator.length>=1?b.add(this.denominator[0]):a&&a.strictUnits||!this.backupUnit||b.add(this.backupUnit)},e.prototype.toString=function(){var a,b=this.numerator.join("*");for(a=0;a0)for(b=0;e>b;b++)this.numerator.push(a);else if(0>e)for(b=0;-e>b;b++)this.denominator.push(a)}0===this.numerator.length&&0===this.denominator.length&&c&&(this.backupUnit=c),this.numerator.sort(),this.denominator.sort()},b.exports=e},{"../data/unit-conversions":11,"./node":64}],74:[function(a,b){var c=a("./node"),d=function(a,b,c,d){this.value=a,this.currentFileInfo=c,this.index=b,this.isEvald=d};d.prototype=new c,d.prototype.type="Url",d.prototype.accept=function(a){this.value=a.visit(this.value)},d.prototype.genCSS=function(a,b){b.add("url("),this.value.genCSS(a,b),b.add(")")},d.prototype.eval=function(a){var b,c=this.value.eval(a);if(!this.isEvald&&(b=this.currentFileInfo&&this.currentFileInfo.rootpath,b&&"string"==typeof c.value&&a.isPathRelative(c.value)&&(c.quote||(b=b.replace(/[\(\)'"\s]/g,function(a){return"\\"+a})),c.value=b+c.value),c.value=a.normalizePath(c.value),a.urlArgs&&!c.value.match(/^\s*data:/))){var e=-1===c.value.indexOf("?")?"?":"&",f=e+a.urlArgs;-1!==c.value.indexOf("#")?c.value=c.value.replace("#",f+"#"):c.value+=f}return new d(c,this.index,this.currentFileInfo,!0)},b.exports=d},{"./node":64}],75:[function(a,b){var c=a("./node"),d=function(a){if(this.value=a,!a)throw new Error("Value requires an array argument")};d.prototype=new c,d.prototype.type="Value",d.prototype.accept=function(a){this.value&&(this.value=a.visitArray(this.value))},d.prototype.eval=function(a){return 1===this.value.length?this.value[0].eval(a):new d(this.value.map(function(b){return b.eval(a)}))},d.prototype.genCSS=function(a,b){var c;for(c=0;c=0&&"\n"!==b.charAt(c);)e++;return"number"==typeof a&&(d=(b.slice(0,a).match(/\n/g)||"").length),{line:d,column:e}}}},{}],78:[function(a,b){var c=a("../tree"),d=a("./visitor"),e=function(){this._visitor=new d(this),this.contexts=[],this.allExtendsStack=[[]]};e.prototype={run:function(a){return a=this._visitor.visit(a),a.allExtends=this.allExtendsStack[0],a},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,d,e,f,g=[],h=a.rules,i=h?h.length:0;for(b=0;i>b;b++)a.rules[b]instanceof c.Extend&&(g.push(h[b]),a.extendOnEveryPath=!0);var j=a.paths;for(b=0;b=0||(i=[k.selfSelectors[0]],g=n.findMatch(j,i),g.length&&j.selfSelectors.forEach(function(a){h=n.extendSelector(g,i,a),l=new c.Extend(k.selector,k.option,0),l.selfSelectors=h,h[h.length-1].extendList=[l],m.push(l),l.ruleset=k.ruleset,l.parent_ids=l.parent_ids.concat(k.parent_ids,j.parent_ids),k.firstExtendOnThisSelectorPath&&(l.firstExtendOnThisSelectorPath=!0,k.ruleset.paths.push(h))}));if(m.length){if(this.extendChainCount++,d>100){var o="{unable to calculate}",p="{unable to calculate}";try{o=m[0].selfSelectors[0].toCSS(),p=m[0].selector.toCSS()}catch(q){}throw{message:"extend circular reference detected. One of the circular extends is currently:"+o+":extend("+p+")"}}return m.concat(n.doExtendChaining(m,b,d+1))}return m},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitSelector:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){if(!a.root){var b,c,d,e,f=this.allExtendsStack[this.allExtendsStack.length-1],g=[],h=this;for(d=0;d0&&k[i.matched].combinator.value!==g?i=null:i.matched++,i&&(i.finished=i.matched===k.length,i.finished&&!a.allowAfter&&(e+1j&&k>0&&(l[l.length-1].elements=l[l.length-1].elements.concat(b[j].elements.slice(k)),k=0,j++),i=f.elements.slice(k,h.index).concat([g]).concat(d.elements.slice(1)),j===h.pathIndex&&e>0?l[l.length-1].elements=l[l.length-1].elements.concat(i):(l=l.concat(b.slice(j,h.pathIndex)),l.push(new c.Selector(i))),j=h.endPathIndex,k=h.endPathElementIndex,k>=b[j].elements.length&&(k=0,j++);return j0&&(l[l.length-1].elements=l[l.length-1].elements.concat(b[j].elements.slice(k)),j++),l=l.concat(b.slice(j,b.length))},visitRulesetOut:function(){},visitMedia:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitMediaOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1},visitDirective:function(a){var b=a.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);b=b.concat(this.doExtendChaining(b,a.allExtends)),this.allExtendsStack.push(b)},visitDirectiveOut:function(){this.allExtendsStack.length=this.allExtendsStack.length-1}},b.exports=f},{"../tree":56,"./visitor":83}],79:[function(a,b){var c=a("../contexts"),d=a("./visitor"),e=function(a,b,e,f,g){if(this._visitor=new d(this),this._importer=a,this._finish=b,this.context=e||new c.Eval,this.importCount=0,this.onceFileDetectionMap=f||{},this.recursionDetector={},g)for(var h in g)g.hasOwnProperty(h)&&(this.recursionDetector[h]=!0)};e.prototype={isReplacing:!0,run:function(a){var b;try{this._visitor.visit(a)}catch(c){b=c}this.isFinished=!0,0===this.importCount&&this._finish(b)},visitImport:function(a,b){var d,f=this,g=a.options.inline;if(!a.css||g){try{d=a.evalForImport(this.context)}catch(h){h.filename||(h.index=a.index,h.filename=a.currentFileInfo.filename),a.css=!0,a.error=h}if(d&&(!d.css||g)){a=d,this.importCount++;var i=new c.Eval(this.context,this.context.frames.slice(0));a.options.multiple&&(i.importMultiple=!0),this._importer.push(a.getPath(),a.currentFileInfo,a.options,function(b,c,d,h){b&&!b.filename&&(b.index=a.index,b.filename=a.currentFileInfo.filename);var j=d||h in f.recursionDetector;i.importMultiple||(a.skip=j?!0:function(){return h in f.onceFileDetectionMap?!0:(f.onceFileDetectionMap[h]=!0,!1)});var k=function(a){f.importCount--,0===f.importCount&&f.isFinished&&f._finish(a)};return!c||(a.root=c,a.importedFilename=h,g||!i.importMultiple&&j)?void k():(f.recursionDetector[h]=!0,void new e(f._importer,k,i,f.onceFileDetectionMap,f.recursionDetector).run(c))})}}return b.visitDeeper=!1,a},visitRule:function(a,b){return b.visitDeeper=!1,a},visitDirective:function(a){return this.context.frames.unshift(a),a},visitDirectiveOut:function(){this.context.frames.shift()},visitMixinDefinition:function(a){return this.context.frames.unshift(a),a},visitMixinDefinitionOut:function(){this.context.frames.shift()},visitRuleset:function(a){return this.context.frames.unshift(a),a},visitRulesetOut:function(){this.context.frames.shift()},visitMedia:function(a){return this.context.frames.unshift(a.rules[0]),a},visitMediaOut:function(){this.context.frames.shift()}},b.exports=e},{"../contexts":8,"./visitor":83}],80:[function(a,b){var c={Visitor:a("./visitor"),ImportVisitor:a("./import-visitor"),ExtendVisitor:a("./extend-visitor"),JoinSelectorVisitor:a("./join-selector-visitor"),ToCSSVisitor:a("./to-css-visitor")};b.exports=c},{"./extend-visitor":78,"./import-visitor":79,"./join-selector-visitor":81,"./to-css-visitor":82,"./visitor":83}],81:[function(a,b){var c=a("./visitor"),d=function(){this.contexts=[[]],this._visitor=new c(this)};d.prototype={run:function(a){return this._visitor.visit(a)},visitRule:function(a,b){b.visitDeeper=!1},visitMixinDefinition:function(a,b){b.visitDeeper=!1},visitRuleset:function(a){var b,c=this.contexts[this.contexts.length-1],d=[];this.contexts.push(d),a.root||(b=a.selectors,b&&(b=b.filter(function(a){return a.getIsOutput()}),a.selectors=b.length?b:b=null,b&&a.joinSelectors(d,c,b)),b||(a.rules=null),a.paths=d)},visitRulesetOut:function(){this.contexts.length=this.contexts.length-1},visitMedia:function(a){var b=this.contexts[this.contexts.length-1];a.rules[0].root=0===b.length||b[0].multiMedia}},b.exports=d},{"./visitor":83}],82:[function(a,b){var c=a("../tree"),d=a("./visitor"),e=function(a){this._visitor=new d(this),this._context=a};e.prototype={isReplacing:!0,run:function(a){return this._visitor.visit(a)},visitRule:function(a){return a.variable?[]:a},visitMixinDefinition:function(a){return a.frames=[],[]},visitExtend:function(){return[]},visitComment:function(a){return a.isSilent(this._context)?[]:a},visitMedia:function(a,b){return a.accept(this._visitor),b.visitDeeper=!1,a.rules.length?a:[]},visitDirective:function(a){if(a.currentFileInfo.reference&&!a.isReferenced)return[];if("@charset"===a.name){if(this.charset){if(a.debugInfo){var b=new c.Comment("/* "+a.toCSS(this._context).replace(/\n/g,"")+" */\n");return b.debugInfo=a.debugInfo,this._visitor.visit(b)}return[]}this.charset=!0}return a.rules&&a.rules.rules&&this._mergeRules(a.rules.rules),a},checkPropertiesInRoot:function(a){for(var b,d=0;d0)&&e.splice(0,0,a);else{a.paths&&(a.paths=a.paths.filter(function(a){var b;for(" "===a[0].elements[0].combinator.value&&(a[0].elements[0].combinator=new c.Combinator("")),b=0;bh;)d=f[h],d&&d.rules?(e.push(this._visitor.visit(d)),f.splice(h,1),g--):h++;g>0?a.accept(this._visitor):a.rules=null,b.visitDeeper=!1,f=a.rules,f&&(this._mergeRules(f),f=a.rules),f&&(this._removeDuplicateRules(f),f=a.rules),f&&f.length>0&&a.paths.length>0&&e.splice(0,0,a)}return 1===e.length?e[0]:e},_removeDuplicateRules:function(a){if(a){var b,d,e,f={};for(e=a.length-1;e>=0;e--)if(d=a[e],d instanceof c.Rule)if(f[d.name]){b=f[d.name],b instanceof c.Rule&&(b=f[d.name]=[f[d.name].toCSS(this._context)]);var g=d.toCSS(this._context);-1!==b.indexOf(g)?a.splice(e,1):b.push(g)}else f[d.name]=d}},_mergeRules:function(a){if(a){for(var b,d,e,f={},g=0;g1){d=b[0];var h=[],i=[];b.map(function(a){"+"===a.merge&&(i.length>0&&h.push(e(i)),i=[]),i.push(a)}),h.push(e(i)),d.value=g(h)}})}}},b.exports=e},{"../tree":56,"./visitor":83}],83:[function(a,b){function c(a){return a}function d(a,b){var c,e;for(c in a)if(a.hasOwnProperty(c))switch(e=a[c],typeof e){case"function":e.prototype&&e.prototype.type&&(e.prototype.typeIndex=b++);break;case"object":b=d(e,b)}return b}var e=a("../tree"),f={visitDeeper:!0},g=!1,h=function(a){this._implementation=a,this._visitFnCache=[],g||(d(e,1),g=!0)};h.prototype={visit:function(a){if(!a)return a;var b=a.typeIndex;if(!b)return a;var d,e=this._visitFnCache,g=this._implementation,h=b<<1,i=1|h,j=e[h],k=e[i],l=f;if(l.visitDeeper=!0,j||(d="visit"+a.type,j=g[d]||c,k=g[d+"Out"]||c,e[h]=j,e[i]=k),j!==c){var m=j.call(g,a,l);g.isReplacing&&(a=m)}return l.visitDeeper&&a&&a.accept&&a.accept(this),k!=c&&k.call(g,a),a},visitArray:function(a,b){if(!a)return a;var c,d=a.length;if(b||!this._implementation.isReplacing){for(c=0;d>c;c++)this.visit(a[c]);return a}var e=[];for(c=0;d>c;c++){var f=this.visit(a[c]);f.splice?f.length&&this.flatten(f,e):e.push(f)}return e},flatten:function(a,b){b||(b=[]);var c,d,e,f,g,h;for(d=0,c=a.length;c>d;d++)if(e=a[d],e.splice)for(g=0,f=e.length;f>g;g++)h=e[g],h.splice?h.length&&this.flatten(h,b):b.push(h);else b.push(e);return b}},b.exports=h},{"../tree":56}],84:[function(a,b){function c(){}var d=b.exports={};d.nextTick=function(){var a="undefined"!=typeof window&&window.setImmediate,b="undefined"!=typeof window&&window.postMessage&&window.addEventListener;if(a)return function(a){return window.setImmediate(a)};if(b){var c=[];return window.addEventListener("message",function(a){var b=a.source;if((b===window||null===b)&&"process-tick"===a.data&&(a.stopPropagation(),c.length>0)){var d=c.shift();d()}},!0),function(a){c.push(a),window.postMessage("process-tick","*")}}return function(a){setTimeout(a,0)}}(),d.title="browser",d.browser=!0,d.env={},d.argv=[],d.on=c,d.addListener=c,d.once=c,d.off=c,d.removeListener=c,d.removeAllListeners=c,d.emit=c,d.binding=function(){throw new Error("process.binding is not supported")},d.cwd=function(){return"/"},d.chdir=function(){throw new Error("process.chdir is not supported")}},{}],85:[function(a,b){"use strict";function c(a){function b(a){return null===i?void k.push(a):void f(function(){var b=i?a.onFulfilled:a.onRejected;if(null===b)return void(i?a.resolve:a.reject)(j);var c;try{c=b(j)}catch(d){return void a.reject(d)}a.resolve(c)})}function c(a){try{if(a===l)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"==typeof a||"function"==typeof a)){var b=a.then;if("function"==typeof b)return void e(b.bind(a),c,g)}i=!0,j=a,h()}catch(d){g(d)}}function g(a){i=!1,j=a,h()}function h(){for(var a=0,c=k.length;c>a;a++)b(k[a]);k=null}if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");var i=null,j=null,k=[],l=this;this.then=function(a,c){return new l.constructor(function(e,f){b(new d(a,c,e,f))})},e(a,c,g)}function d(a,b,c,d){this.onFulfilled="function"==typeof a?a:null,this.onRejected="function"==typeof b?b:null,this.resolve=c,this.reject=d}function e(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var f=a("asap");b.exports=c},{asap:87}],86:[function(a,b){"use strict";function c(a){this.then=function(b){return"function"!=typeof b?this:new d(function(c,d){e(function(){try{c(b(a))}catch(e){d(e)}})})}}var d=a("./core.js"),e=a("asap");b.exports=d,c.prototype=d.prototype;var f=new c(!0),g=new c(!1),h=new c(null),i=new c(void 0),j=new c(0),k=new c("");d.resolve=function(a){if(a instanceof d)return a;if(null===a)return h;if(void 0===a)return i;if(a===!0)return f;if(a===!1)return g;if(0===a)return j;if(""===a)return k;if("object"==typeof a||"function"==typeof a)try{var b=a.then;if("function"==typeof b)return new d(b.bind(a))}catch(e){return new d(function(a,b){b(e)})}return new c(a)},d.all=function(a){var b=Array.prototype.slice.call(a);return new d(function(a,c){function d(f,g){try{if(g&&("object"==typeof g||"function"==typeof g)){var h=g.then;if("function"==typeof h)return void h.call(g,function(a){d(f,a)},c)}b[f]=g,0===--e&&a(b)}catch(i){c(i)}}if(0===b.length)return a([]);for(var e=b.length,f=0;f